精华内容
下载资源
问答
  • java 容器都有哪些?
    万次阅读 多人点赞
    2019-07-27 18:36:57

    容器可以说是Java Core中比较重要的一部分了。

    数组,String,java.util下的集合容器

    ==============================================================================

    数组长度限制为 Integer.Integer.MAX_VALUE;

    String的长度限制: 底层是char 数组 长度 Integer.MAX_VALUE 线程安全的

    java.util下的集合容器

    这一块之所以重要是因为,各个接口的特性不同。下面说一下我对这些类的理解。

    Set下各种实现类对比

    HashSet基于哈希表实现,有以下特点:

              1.不允许重复

               2.允许值为null,但是只能有一个

               3.无序的。

               4.没有索引,所以不包含索引操作的方法

    LinkedHashSet跟HashSet一样都是基于哈希表实现。只不过linkedHashSet在hashSet的基础上多了一个链表,这个链表就是用来维护容器中每个元素的顺序的。有以下特点:

               1.不允许重复

               2.允许值为null,但是只能有一个

               3.有序的。

               4.没有索引,所以不包含索引操作的方法

    TreeSet是SortedSet接口的唯一实现类,是基于二叉树实现的。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。有以下特点:

               1.不允许重复

               2.不允许null值

               3.没有索引,所以不包含索引操作的方法

    List下各种实现类对比。(这几个类都是有序的,允许重复的)

    ArrayList是基于数组实现的,其特点是查询快,增删慢。

            查询快是因为数组的空间是连续的,查询时只要通过首地址和下标很快就能找到元素。

            增删慢是因为数组是不能扩容的,一旦增加或者删除元素,内部操作就是新开辟一个数组把元素copy到新的数组,老的数   组等待被垃圾回收。

    以元素增加为例,我们看一下内部实现的源码:

    LinkedList是基于链表实现的。相比于ArrayList其特点是查询慢,增删快。

    查询慢:因为链表在内存中开辟的空间不一定是连续的(基本上不可能是连续的)所以链表实现的方式是每个元素节点都会存放自己的地址,数据以及下一个节点的地址,这样把所有的元素连接起来。所以当要查询元素时只能一个一个的往下找,相比于数组的首地址加下标会慢上不少。

    下面是链表的数据存储方式:假设有三个元素

    Vector也是基于数组实现的,相比于arrayList它是线程安全的。如果不考虑线程安全它,ArrayList性能更优。

    Map是双列集合的超类。也就是键值对形式。

    HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

    • HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
    • HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
    • 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
    • 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
    • HashMap不能保证随着时间的推移Map中的元素次序是不变的。

    LinkedHashMap和hashMap的区别在于多维护了一个链表,用来存储每一个元素的顺序,就跟HashSet和LinkedHashSet差不多。

    HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap。

    欢迎加微信一起学习交流

    相关面试题可以查看      面试题系列

    更多相关内容
  • 深入理解Java中容器

    万次阅读 多人点赞 2016-07-27 17:25:40
    1、容器的概念 在Java当中,如果有一个类专门用来存放其它类的对象,这个类就叫做容器,或者就叫做集合,集合就是将...1、容器不是数组,不能通过下标的方式访问容器的元素 2、数组的所有功能通过Arraylist容器

    1、容器的概念
    在Java当中,如果有一个类专门用来存放其它类的对象,这个类就叫做容器,或者就叫做集合,集合就是将若干性质相同或相近的类对象组合在一起而形成的一个整体
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    2、容器与数组的关系
    之所以需要容器:
    1、数组的长度难以扩充
    2、数组中数据的类型必须相同
    容器与数组的区别与联系:
    1、容器不是数组,不能通过下标的方式访问容器中的元素
    2、数组的所有功能通过Arraylist容器都可以实现,只是实现的方式不同
    3、如果非要将容器当做一个数组来使用,通过toArray方法返回的就是一个数组
    示例程序:

    package IT;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    //数组的所有功能通过ArrayList容器都可以实现,只是实现的方式不同
    public class App
    {
         public static void main(String[] args)
         {
        	   ArrayList<Integer> arrayList = new ArrayList<Integer>();
        	   arrayList.add(12);
        	   arrayList.add(10);
        	   arrayList.add(35);
        	   arrayList.add(100);
        	   
        	   Iterator<Integer> iterator = arrayList.iterator();//获取容器的迭代器
        	   while(iterator.hasNext())
        	   {
        		   Integer value = iterator.next();//获取当前游标右边的元素,同时游标右移-->
        		   System.out.println(value);
        	   }
        	   System.out.println("通过ArrayList容器获取一个数组arr:");
        	   Object[] arr = arrayList.toArray();
        	   for(int i=0;i<arr.length;i++)
        	   {
        		   System.out.println(arr[i]);
        	   }
         }
    }
    

    输出结果:

    12
    10
    35
    100
    通过ArrayList容器获取一个数组arr:
    12
    10
    35
    100
    

    3、容器常用的几个方法

    boolean add(Object obj):向容器中添加指定的元素
    Iterator iterator():返回能够遍历当前集合中所有元素的迭代器
    Object[] toArray():返回包含此容器中所有元素的数组。
    Object get(int index):获取下标为index的那个元素
    Object remove(int index):删除下标为index的那个元素
    Object set(int index,Object element):将下标为index的那个元素置为element
    Object add(int index,Object element):在下标为index的位置添加一个对象element
    Object put(Object key,Object value):向容器中添加指定的元素
    Object get(Object key):获取关键字为key的那个对象
    int size():返回容器中的元素数
    

    在这里插入图片描述
    在这里插入图片描述
    实例程序:

    package IT;
    
    import java.util.ArrayList;
    
    public class App
    {
         public static void main(String[] args)
         {
        	   ArrayList<Integer> arrayList = new ArrayList<Integer>();
        	   arrayList.add(12);
        	   arrayList.add(10);
        	   arrayList.add(35);
        	   arrayList.add(100);
        	   System.out.println("原容器中的元素为:");
        	   System.out.println(arrayList);
        	   System.out.println("\n");
        	   
        	   /*******重置set(int index,Object element)*******/
        	   System.out.println("将下标为1位置的元素置为20,将下标为2位置的元素置为70");
               arrayList.set(1, 20);
               arrayList.set(2, 70);
               System.out.println("重置之后容器中的元素为:");
               System.out.println(arrayList);
               System.out.println("\n");
               
               /*******中间插队add(int index,Object element)*******/
               System.out.println("在下标为1的位置插入一个元素,-----插入元素:此时容器后面的元素整体向后移动");
               arrayList.add(1, 80);//在下标为1的位置插入一个元素,此时容量加1,-----位置后面的元素整体向后移动
               System.out.println("插入之后容器中的元素为:");
               System.out.println(arrayList);
               System.out.println("插入之后容器中的容量为:");
               System.out.println(arrayList.size());
               System.out.println("\n");
               
               
               /*******中间删除元素remove(int index)*******/
               System.out.println("将下标为3位置的元素70删除,-----删除元素:此时容器位置后面的元素整体向前移");
               arrayList.remove(3);
               System.out.println("删除之后容器中的元素为:");
               System.out.println(arrayList);
               System.out.println("删除之后容器中的容量为:");
               System.out.println(arrayList.size());
               
         }
    }
    

    运行结果:

    原容器中的元素为:
    [12, 10, 35, 100]
    
    
    将下标为1位置的元素置为20,将下标为2位置的元素置为70
    重置之后容器中的元素为:
    [12, 20, 70, 100]
    
    
    在下标为1的位置插入一个元素,-----插入元素:此时容器后面的元素整体向后移动
    插入之后容器中的元素为:
    [12, 80, 20, 70, 100]
    插入之后容器中的容量为:
    5
    
    
    将下标为3位置的元素70删除,-----删除元素:此时容器位置后面的元素整体向前移
    删除之后容器中的元素为:
    [12, 80, 20, 100]
    删除之后容器中的容量为:
    4
    

    4、容器的分类
    容器分为Set集、List列表、Map映射
    Set集合:由于内部存储结构的特点,Set集合中不区分元素的顺序(即使存放的类实现了compareTo方法,也是没用的),不允许出现重复的元素(用户自定义的类有的时候需要实现相应方法),TreeSet容器特殊,元素放进去的时候自然而然就有顺序了,Set容器可以与数学中的集合相对应:相同的元素不会被加入。
    在这里插入图片描述
    在这里插入图片描述
    List列表:由于内部存储结构的特点,List集合中区分元素的顺序,且允许包含重复的元素。List集合中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素—有序,可以重复
    Map映射:由于内部存储结构的特点,映射中不能包含重复的键值,每个键最多只能映射一个值,否则会出现覆盖的情况(后面的value值会将前面的value值覆盖掉),Map是一种把键对象和值对象进行映射的集合,即Map容器中既要存放数据本身,也要存放关键字:相同的元素会被覆盖
    在这里插入图片描述
    在这里插入图片描述
    注意:对于Set和Map来说,元素放进去之后是没有顺序的,如果希望元素放进去之后是有顺序的,可以用treeSet和treeMap存储数据。
    实例程序:

           var set2 = mutable.Set.empty[Int]
           set2 += 10
           set2 ++= List(50,100,200)
           set2 += 500
           println("Set输出的结果:")
           println(set2)
           var map3 = mutable.Map.empty[String,Double]
           map3 += "Spark"->90.0
           map3 += "Hadoop"->80.0
           map3 ++= List("Scala"->100.0,"Java"->60.0)
           println("Map输出的结果:")
           println(map3)
    

    运行结果:

    Set输出的结果:
    Set(100, 50, 500, 10, 200)
    Map输出的结果:
    Map(Hadoop -> 80.0, Spark -> 90.0, Scala -> 100.0, Java -> 60.0)
    

    实例程序:

           var treeSet = TreeSet(10,20,30,90,100,200,50)
           println(treeSet)
           /*键值对排序是根据key的值进行排序的,没有value的事情,让我联想到了MapReduce中排序的时候之所以根据k2
           而不是v2的值进行排序,这是因为哈希映射内部决定的,而不是MapReduce决定的
           呵呵!注意:排序区分大小写的哦!!!*/
           var treeSet2 = TreeSet[String]("Spark","Anang","Baby","Hello")
           println(treeSet2)
           var treeMap = TreeMap[String,Integer]("Java"->100,"Scala"->88,"Python"->60,"Anglebaby"->500)
           println(treeMap)
    

    运行结果:

    TreeSet(10, 20, 30, 50, 90, 100, 200)
    TreeSet(Anang, Baby, Hello, Spark)
    Map(Anglebaby -> 500, Java -> 100, Python -> 60, Scala -> 88)
    

    示例程序:

    class Student implements Comparable<Object>
    {
    	public int id;
    	public String nameString;
    	
    	public Student(int id,String name)
    	{
    		this.id = id;
    		this.nameString = name;
    	}
    	
    	
    	@Override 
    	public String toString()
    	{
    		return this.id + " " + this.nameString;
    	}
    	
    	
    	@Override
    	public int compareTo(Object obj)
    	{
    		Student stu = (Student)obj;
    		if (this.id > stu.id)
    		{
    			return 1;
    		}
    		else if (this.id == stu.id)
    		{
    			return 0;
    		}
    		else {
    			return -1;
    		}
    	}
    
    }
    
    
    public class Test_ArrayList_1
    {
        public static void main(String[] args)
        {
               HashSet<Integer> hashSet1 = new HashSet<Integer>();
    	       hashSet1.add(100);
    	       hashSet1.add(200);
    	       hashSet1.add(200);
    	       hashSet1.add(400);
    	       hashSet1.add(10);
    	       hashSet1.add(20);
    	       hashSet1.add(8000);
    	       hashSet1.add(1000);
    	       hashSet1.add(2000);
    	       hashSet1.add(2000);
    	       hashSet1.add(4000);
    	       hashSet1.add(100);
    	       hashSet1.add(200);
    	       hashSet1.add(8000);
               for (Integer integer : hashSet1)
    		   {
    			  System.out.println(integer);   //打印出的类不含有重复的元素
    		   }
               
               System.out.println("--------------------");
               
               
               HashSet<Student> hashSet2 = new HashSet<Student>();     //此时Student有一个问题,没有实现equals方法和hashCode方法.
               hashSet2.add(new Student(1000, "wtt"));
               hashSet2.add(new Student(2000, "wtt"));
               hashSet2.add(new Student(3000, "wtt"));
               hashSet2.add(new Student(3000, "wtt"));
               hashSet2.add(new Student(100, "wtt"));
               hashSet2.add(new Student(50, "wtt"));
               hashSet2.add(new Student(10, "wtt"));
               
               for (Student student : hashSet2)
    		   {
    			  System.out.println(student);          //尽管Student类实现了compareTo方法,也是没用的,每次打印出元素的顺序是不一样的.
    		   }
               
               System.out.println("--------------------");
               
        }
    }
    
    /*
     *  1000
    	2000
    	100
    	4000
    	20
    	200
    	10
    	400
    	8000
    	--------------------
    	3000 wtt
    	1000 wtt
    	100 wtt
    	10 wtt
    	3000 wtt
    	2000 wtt
    	50 wtt
    	--------------------
     * */
    
    

    这里写图片描述

    5、toString()方法的使用:凡是把类对象放到容器中,相应的类都应该实现Object类中的toString()方法;凡是Java中自带的数据类型,都已经重写完了toString()方法
    实例1:(未重写toString()方法之前)

    package IT;
    
    
    public class App
    {
         public static void main(String[] args)
         {
               //Java中自带的类
        	  System.out.println("-----凡是Java中自带的数据类型都已经重写完了toString()方法!---");
        	  System.out.println(new Integer(2).toString());
        	  System.out.println(new String("zhang").toString());
        	  //用户自定义的类Student
        	  System.out.println(new Student("zhangsan",99.8).toString());
        	  
         }
    }
    class Student
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
    }
    

    输出结果:

    -----凡是Java中自带的数据类型都已经重写完了toString()方法!---
    2
    zhang
    IT.Student@1af2f973
    

    实例2:(重写完toString()方法之后)

    package IT;
    
    import java.util.ArrayList;
    
    public class App
    {
         public static void main(String[] args)
         {
             ArrayList<Student> arr = new ArrayList<Student>();
             arr.add(new Student("zhangsan",89.8));
             arr.add(new Student("lisi",90));
             arr.add(new Student("wangwu",60.6));
             
             System.out.println(arr);
         }
    }
    class Student
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    }
    

    输出结果:

    [zhangsan	89.8, lisi	90.0, wangwu	60.6]
    

    6、Comparable接口中的compareTo()方法:凡是需要进行比较排序的类都应该实现Comparable接口中的compareTo()方法;凡是把类对象放到以树为内部结构的容器中都应该实现Comparable接口中的compareTo()方法
    在这里插入图片描述
    实例1:

    package IT;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class App
    {
         public static void main(String[] args)
         {
             ArrayList<Student> arr = new ArrayList<Student>();
             arr.add(new Student("zhangsan",89.8));
             arr.add(new Student("lisi",90));
             arr.add(new Student("wangwu",60.6));
             arr.add(new Student("wangting",85.6));
             
             Collections.sort(arr);
             
             for (Student student : arr)
    		{
    			System.out.println(student);
    		}
             
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      return (int) (this.score - obj.score);//比较的标准为score进行升序
    	}
    }
    

    输出结果:

    wangwu	60.6
    wangting	85.6
    zhangsan	89.8
    lisi	90.0
    

    实例2:

    package IT;
    
    
    import java.util.TreeSet;
    
    public class App
    {
         public static void main(String[] args)
         {
                TreeSet<Student> treeSet = new TreeSet<Student>();
                treeSet.add(new Student("wangwu",60.6));
                treeSet.add(new Student("lisi",90.0));
                treeSet.add(new Student("wangting",85.6));
                treeSet.add(new Student("zhangsan",60.6));
                
                for (Student student : treeSet)
    			{
    				System.out.println(student);
    			}
             
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      if(this.score > obj.score)
    	    	  return 1;
    	      else
    	    	  return -1;
    	}
    }
    

    输出结果:

    zhangsan	60.6
    wangwu	60.6
    wangting	85.6
    lisi	90.0
    

    7、凡是把类对象放到以哈希表为内部存储结构的容器中,相应的类必须要实现equals方法和hashCode方法,这样才符合哈希表真实的逻辑功能.(对于咱们自己定义的类,如果你没有重写hashcode方法,我们可以通过hashcode方法获取该对象的内存地址)
    在这里插入图片描述
    简而言之:哈希表先根据它的hashcode方法提供的哈希码找到存储的位置,在从位置所关联的链表里面寻找是否有相同的对象,如果有相同的对象,则不存放,如果没有,则存放进去。
    在这里插入图片描述
    在这里插入图片描述
    如果你还不懂,看我这篇文章吧:https://wenku.baidu.com/view/a1f1c88ce518964bcf847cd0
    实例程序1:(为重写之前)

    package IT;
    
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Set;
    
    public class App
    {
         public static void main(String[] args)
         {
               //Java中自带的数据类型
        	  System.out.println("先测试Java中自带的数据类型:");
        	  HashMap<String, Double> hashMap1 = new HashMap<String,Double>();
        	  hashMap1.put("zhangsan", 96.0);
        	  hashMap1.put("lisi", 88.6);
        	  hashMap1.put("wangwu", 98.6);
        	  hashMap1.put("wangting",  87.5);
        	  hashMap1.put("zhangsan", 96.0);
        	  hashMap1.put("lisi", 88.6);
        	  hashMap1.put("wangwu", 98.6);
        	  hashMap1.put("wangting",  87.5);
        	  
        	  Set<String> keySet = hashMap1.keySet();
        	  Iterator<String> iterator = keySet.iterator();
        	  while(iterator.hasNext())
        	  {
        		  String key = iterator.next();
        		  System.out.println(key+"\t"+hashMap1.get(key));
        	  }
        	  System.out.println("Java中自带的数据类型:相同的对象会覆盖!");
        	  System.out.println("\n");
        	  //用户自定义的数据类型:为重写之前
        	  System.out.println("测试用户自定义的数据类型--未重写两个方法之前:");
        	  HashMap<Student, String> hashMap2 = new HashMap<Student,String>();
        	  hashMap2.put(new Student("zhangsan",88.8), "beijing");
        	  hashMap2.put(new Student("lisi",88.8), "beijing");
        	  hashMap2.put(new Student("wangwu",66.9), "beijing");
        	  hashMap2.put(new Student("zhangsan",88.8), "beijing");
        	  hashMap2.put(new Student("lisi",88.8), "beijing");
        	  hashMap2.put(new Student("wangwu",66.9), "beijing");
        	  Set<Student> keySet2 = hashMap2.keySet();
        	  Iterator<Student> iterator2 = keySet2.iterator();
        	  while(iterator2.hasNext())
        	  {
        		  Student key = iterator2.next();
        		  System.out.println(key+"\t"+hashMap2.get(key));
        	  }
        	  System.out.println("如果没有重写:导致相同的对象不会被覆盖!");
        	  
        	 
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      if(this.score > obj.score)
    	    	  return 1;
    	      else
    	    	  return -1;
    	}
    }
    

    输出结果:

    先测试Java中自带的数据类型:
    wangting	87.5
    wangwu	98.6
    lisi	88.6
    zhangsan	96.0
    Java中自带的数据类型:相同的对象会覆盖!
    
    
    测试用户自定义的数据类型--为重写两个方法之前:
    zhangsan	88.8	beijing
    wangwu	66.9	beijing
    lisi	88.8	beijing
    wangwu	66.9	beijing
    zhangsan	88.8	beijing
    lisi	88.8	beijing
    如果没有重写:导致相同的对象不会被覆盖!
    

    实例程序2:重写之后

    package IT;
    
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Set;
    
    public class App
    {
         public static void main(String[] args)
         {
        	  //用户自定义的数据类型:为重写之后
        	  System.out.println("测试用户自定义的数据类型--重写两个方法之后:");
        	  HashMap<Student, String> hashMap2 = new HashMap<Student,String>();
        	  hashMap2.put(new Student("zhangsan",88.8), "beijing");
        	  hashMap2.put(new Student("lisi",88.8), "beijing");
        	  hashMap2.put(new Student("wangwu",66.9), "beijing");
        	  hashMap2.put(new Student("zhangsan",88.8), "beijing");
        	  hashMap2.put(new Student("lisi",88.8), "beijing");
        	  hashMap2.put(new Student("wangwu",66.9), "beijing");
        	  Set<Student> keySet2 = hashMap2.keySet();
        	  Iterator<Student> iterator2 = keySet2.iterator();
        	  while(iterator2.hasNext())
        	  {
        		  Student key = iterator2.next();
        		  System.out.println(key+"\t"+hashMap2.get(key));
        	  }
        	  System.out.println("重写过后:相同的对象会被覆盖!");
        	  
        	 
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      if(this.score > obj.score)
    	    	  return 1;
    	      else
    	    	  return -1;
    	}
    	@Override
    	public int hashCode()
    	{
    		return (int) (this.name.hashCode()*score);//保证相同对象映射到同一个索引位置
    	}
    	@Override
    	public boolean equals(Object obj)
    	{
    		Student cc = (Student)obj;
    		return this.name==cc.name&&this.score==cc.score;
    	}
    }
    

    输出结果:

    测试用户自定义的数据类型--重写两个方法之后:
    wangwu	66.9	beijing
    zhangsan	88.8	beijing
    lisi	88.8	beijing
    重写过后:相同的对象会被覆盖!
    

    8、重要的一个逻辑:逻辑上来讲,只要两个对象的内容相同,其地址(hashCode()返回值)以及这两个对象就应该相同(equals()),
    实例程序(为重写之前):

    package IT;
    
    public class App
    {
         public static void main(String[] args)
         {
        	 //Java中自带的数据类型
        	 System.out.println(new Integer(1).equals(new Integer(1)));
        	 System.out.println(new Integer(1).hashCode()==new Integer(1).hashCode());
        	 System.out.println(new String("zhang").equals(new String("zhang")));
        	 System.out.println(new String("zhang").hashCode()==new String("zhang").hashCode());
        	 
             System.out.println("\n");
             
             //用户自定义的数据类型
             
        	 System.out.println(new Student("zhangsan",98.8).equals(new Student("zhangsan",98.8)));
        	 System.out.println(new Student("zhangsan",98.8).hashCode());
        	 System.out.println(new Student("zhangsan",98.8).hashCode());
        	 
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      if(this.score > obj.score)
    	    	  return 1;
    	      else
    	    	  return -1;
    	}
    }
    

    输出结果:

    true
    true
    true
    true
    
    
    false
    488676694
    1211729930
    

    重写之后:

    package IT;
    
    public class App
    {
         public static void main(String[] args)
         {
        	 System.out.println(new Student("zhangsan",98.8).equals(new Student("zhangsan",98.8)));
        	 System.out.println(new Student("zhangsan",98.8).hashCode());
        	 System.out.println(new Student("zhangsan",98.8).hashCode());
        	  
        	 
         }
    }
    class Student implements  Comparable<Student>
    {
         public String name;
         public double score;
         public Student(String name,double score)
         {
        	 this.name = name;
        	 this.score = score;
         }
         public String toString()
         {
        	 return this.name+"\t"+this.score;
         }
    	public int compareTo(Student obj)
    	{
    	      if(this.score > obj.score)
    	    	  return 1;
    	      else
    	    	  return -1;
    	}
    	@Override
    	public int hashCode()
    	{
    		return (int) (this.name.hashCode()*score);
    	}
    	@Override
    	public boolean equals(Object obj)
    	{
    		Student cc = (Student)obj;
    		return this.name==cc.name&&this.score==cc.score;
    	}
    }
    

    输出结果:

    true
    -2147483648
    -2147483648
    

    上面的5、6、7、8可以归结为4个"凡是",1个“逻辑”:
    1、凡是把类对象放到容器中,相应的类都应该实现Object类中的toString()方法;
    2、凡是需要进行比较排序的类都应该实现Comparable接口中的compareTo()方法;凡是把类对象放到以树为内部结构的容器中都应该实现Comparable接口中的compareTo()方法
    3、凡是把类对象放到以哈希表为内部存储结构的容器中,相应的类必须要实现equals方法和hashCode方法,这样才符合哈希表真实的逻辑功能.
    4、逻辑上来讲,只要两个对象的内容相同,其地址(hashCode()返回值)以及这两个对象就应该相同(equals())。
    9、哈希冲突的相关概念
    在这里插入图片描述
    本质上讲就是:hash(对象1.hashCode())=hash2(对象2.hashCode()),即第一个对象的hashCode()方法返回的哈希码值带入到哈希函数后得到的索引位置与第二个对象的hashCode()方法返回的哈希码值带入到哈希函数后得到的索引位置相同,这就是哈希冲突。
    最常见的哈希算法是取模法。
    下面简单讲讲取模法的计算过程。
    比如:数组的长度是5。这时有一个数据是6。那么如何把这个6存放到长度只有5的数组中呢。按照取模法,计算6%5,结果是1,那么就把6放到数组下标是1的位置。那么,7
    就应该放到2这个位置。到此位置,哈斯冲突还没有出现。这时,有个数据是11,按照取模法,11%5=1,也等于1。那么原来数组下标是1的地方已经有数了,是6。这时又计算出1这个位置,那么数组1这个位置,就必须储存两个数了。这时,就叫哈希冲突。冲突之后就要按照顺序来存放了。
    如果数据的分布比较广泛,而且储存数据的数组长度比较大。
    那么哈希冲突就比较少。否则冲突是很高的。
    10、iterator接口的作用
    在这里插入图片描述
    在这里插入图片描述
    重要方法:

    boolean hasNext():是用来判断当前游标(迭代器)的后面是否存在元素,如果存在返回真,否则返回假
    Object next():先返回当前游标右边的元素,然后游标后移一个位置
    void remove():不推荐使用iterator的remove()方法,而是推荐使用容器自带的remove方法。
    

    在这里插入图片描述
    实例程序:

    package IT;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Set;
    
    public class App
    {
         public static void main(String[] args)
         {
        	  HashMap<String, Double> hashMap = new HashMap<String,Double>();
        	  hashMap.put("zhangsan", 88.6);
        	  hashMap.put("lisi", 69.0);
        	  hashMap.put("wanqwu", 100.0);
        	  hashMap.put("lisi", 69.0);
        	  
        	  Set<String> keySet = hashMap.keySet();
        	  Iterator<String> iterator = keySet.iterator();
        	  while(iterator.hasNext())
        	  {
        		  String key = iterator.next();//获取迭代器右边的元素,同时右移
        		  System.out.println(key+hashMap.get(key));
        	  }
        	  
        	 
         }
    }
    

    思考题:

    package IT;
    
    
    import java.util.TreeSet;
    
    public class App
    {
         public static void main(String[] args)
         {
                 TreeSet<Student> treeSet = new TreeSet<Student>();
               
             
                 treeSet.add(new Student("zhangsan",98));
                 treeSet.add(new Student("zhangsan",98));
                 System.out.println(treeSet.size());
                 System.out.println(treeSet);
        	 
                 //本程序中并没有重写equals方法,但是treeSet将识别出两个new Student("zhangsan",98)为相同的,因为内部数据结构吗?
                 System.out.println(new Student("zhangsan",98).equals(new Student("zhangsan",98)));
     
         }
    }
    class Student implements Comparable<Object>
    {
    	  public String name;
    	  public double score;
    	  public Student(String name,double score)
    	  {
    		  this.name = name;
    		  this.score = score;
    	  }
    	  public String toString()
    	  {
    		  return name + "\t" + score;
    	  }
    	@Override
    	public int compareTo(Object obj)
    	{
    		Student cc = (Student)obj;
    		return (int) (this.score - cc.score);
    	}
    
    }
    

    程序3:

    public class Test_iterator
    {
        public static void main(String[] args)
    	{
    		ArrayList<Integer> arrayList = new ArrayList<Integer>();
    		arrayList.add(10);
    		arrayList.add(20);
    		arrayList.add(30);
    		
    		Iterator<Integer> iterator = arrayList.iterator();
    		
    		
    		while (iterator.hasNext())
    		{
    			Integer aaInteger = iterator.next();
    			System.out.println(aaInteger);
    			
    		}
    	}
    }
    
    
    /**
     *  10
    	20
    	30
     */
    

    上面的讲解如有问题,欢迎留言指正!

    展开全文
  • JAVA常见容器

    万次阅读 多人点赞 2019-02-13 14:59:23
    假设上面已经有了各个容器的继承关系,我们就顺着继承关系说一下各个接口或者类的特点吧。 Iterable 接口 Iterable是一个超级接口,被Collection所继承。它只有一个方法: Iterator&amp;lt;T&amp;gt...

    本文主要介绍JAVA中常见容器间的关系和主要区别。JAVA中的容器种类很多,且各有特点。为此特意进行学习研究,写下此文,作为一点总结。若有错误,欢迎拍砖。
    JAVA各容器继承关系

    上图是JAVA常见的各个容器的继承关系,我们就顺着继承关系说一下各个接口或者类的特点吧。


    Iterable 接口

    • Iterable是一个超级接口,被Collection所继承。它只有一个方法: Iterator<T> iterator() //即返回一个迭代器

    • 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为**“轻量级”**对象,因为创建它的代价小。

    • Java中的Iterator功能比较简单,并且只能单向移动
        (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
        (2) 使用next()获得序列中的下一个元素。
        (3) 使用hasNext()检查序列中是否还有元素。
        (4) 使用remove()将迭代器新返回的元素删除。

    • Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
        举一个例子来说明迭代器的用法:

    public static void main(String args[]) {
    		List<String> l = new ArrayList<String>();
    		l.add("aa");
    		l.add("bb");
    		l.add("cc");
    		Iterator iter = l.iterator();
    		while(iter.hasNext()){
    			System.out.println((String)iter.next());
    		}
    //	for循环的版本
    //		for(Iterator<String> iter=l.iterator();iter.hasNext();){
    //			String str = (String)iter.next();
    //			System.out.println(str);
    //		}  			
    	}
    

    运行结果:
    运行结果

    很明显,iterator用于while循环更方便简洁一些。


    Collection 接口

    我们直接打开API文档进行查看
    Collection接口 文档中写道,JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。也就是一般不会直接使用Collection,而是会使用它的子类,如List或Set。

    在图中我标注了4点,不同的Collection子类对于有序性、重复性、null、线程同步都有不同的策略。基于此,Collection的介绍就这样,下面就其具体的子类来进行介绍。



    List 接口

    • List是有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

    • 用户插入的顺序或者指定的位置就是元素插入的位置。它与Set不同,List允许插入重复的值

    • List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator 接口提供的正常操作外,该迭代器还允许元素插入替换,以及双向访问。还提供了一个方法(如下)来获取从列表中指定位置开始的列表迭代器。

    ListIterator <E> listIterator(int index)
    返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始

    • List 接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。在很多实现中,它们将执行高开销的线性搜索。

    • List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。

    • List的子类

      • List最流行的实现类有Vector、ArrayList、LinkedList。三者的区别将在下文提及。
      • 1.1) ArrayList (类)
      1. ArrayLis是基于数组实现的List类,它封装了一个动态的、增长的、允许再分配的Object[ ]数组.它允许对元素进行快速随机访问
      2. 当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
      • 1.2)Vector (类)
      1. Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList。所以现在已经不太常用了
        1.2.1)Stack (类)
        Stack是Vector提供的一个子类,用于模拟"栈"这种数据结构(LIFO后进先出)
      • 1.3)LinkedList (类)
      1. LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。
      2. 另外,它还实现了Deque接口,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

      关于ArrayList和Vector的区别,再次回顾一下:

      名称扩容方式(默认)线程安全速度有效个数的属性
      ArrayList增长50%线程不安全size
      Vector增长一倍线程安全elementCount
      共同点如果新增的有效元素个数超过数组本身的长度,都会导致数组进行扩容-remove,add(index,obj)方法都会导致内部数组进行数据拷贝的操作,这样在大数据量时,可能会影响效率-

    写在后面:List接口的排序可以通过Collections.sort()来进行定制排序
    只需要继承Comparable接口后,重写compareTo()**方法
    代码如下:

    	public class Student extends Thread implements Comparable {	
       private int age;
       private String name;
       private String tel;
       private int height;
    
       public Student( String name,int age, String tel, int height) {
       	this.age = age;
       	this.name = name;
       	this.tel = tel;
       	this.height = height;
       }
    
       public static void main(String args[]) throws InterruptedException{
       	Student stu1 = new Student("张三",21,"156482",172);
       	Student stu2 = new Student("李四",18,"561618",168);
       	Student stu3 = new Student("王五",19,"841681",170);
       	Student stu4 = new Student("赵七",20,"562595",180);
       	
       	List<Student> list = new ArrayList<Student>();
       	//乱序插入
       	list.add(stu3);
       	list.add(stu1);
       	list.add(stu4);
       	list.add(stu2);
       	
       	System.out.println("-----------排序前----------");
       	Iterator<Student> it = list.iterator();
       	while(it.hasNext()){
       		System.out.println(it.next());
       	}
       	/*
       	 * 使用Collections的sort方法让集合排序,使用其方法必须要重写
       	 * Comparable接口的compareTo()方法
       	 * */
       	Collections.sort(list);
       	System.out.println("-----------按照年龄排序后----------");
       	for(int i=0;i<list.size();i++){
       		System.out.println(list.get(i).toString());
       	}
       }
    
       //重写compareTo方法,用age来比较。也可以用别的来比较
       @Override
       public int compareTo(Object o) {
       	//使用当前对象的年龄和其他对象的年龄比较,如果<0返回负数,>0返回正数,=0返回0
       	int z = this.age - ((Student)o).getAge();
       	if(z<0) 
       		return -1;  // 返回其他负数也行
       	else if(z == 0) 
       		return 0;
       	else 
       		return 1;  //返回其他正数也行
       }
       
       //重写toString,便于输出
       @Override
       public String toString(){
       	return name+","+age+","+tel+","+height;
       }
    
    

    输出结果如下:
    在这里插入图片描述



    Set接口

    • Set,顾名思义,集合的意思。java的集合和数学的集合一样,满足集合的无序性,确定性,单一性。所以可以很好的理解,Set是无序、不可重复的。同时,如果有多个null,则不满足单一性了,所以Set只能有一个null。
    • Set类似于一个罐子,丢进Set的元素没有先后的差别
    • Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,我们在加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行注意equals比较都返回false,则Set就会接受这个新元素对象,否则拒绝。
      ——因为Set的这个制约,在使用Set集合的时候,应该注意两点:
      1. 为Set集合里的元素的实现类实现一个有效的equals(Object)方法;
      2. 对Set的构造函数,传入的Collection参数不能包含重复的元素。
    • Set的子类

      • Set最流行的实现类有HashSet、TreeSet、LinkedHashSet(从HashSet继承而来)。

      • 1.1) HashSet (类)
      1. HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的 hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。
      2. 值得主要的是,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等
        1.1.1)LinkedHashSet(类)
        1. LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
        2. 当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。
        3. LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)
      • 1.2) SortedSet (接口):
        此接口主要用于排序操作,实现了此接口的子类都属于排序的子类
        • 1.2.1)TreeSet(类)
        • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态

      • 1.3) EnumSet (类)
      • EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,


    Queue 接口

    此接口用于模拟“队列”数据结构(FIFO)。新插入的元素放在队尾,队头存放着保存时间最长的元素。

    • Queue的子类、子接口

      • 1.1) PriorityQueue—— 优先队列(类)
      • 其实它并没有按照插入的顺序来存放元素,而是按照队列中某个属性的大小来排列的。故而叫优先队列。

      • 1.2) Deque——双端队列(接口)
        • 1.2.1)ArrayDeque(类)
          基于数组的双端队列,类似于ArrayList有一个Object[] 数组。
        • 1.2.2)LinkedList (类)(上文已有,略)

      简单回顾一下上述三个接口的区别
    容器名是否有序是否可重复null的个数
    List有序可重复允许多个null
    Set无序不可重复只允许一个null
    Queue有序(FIFO)可重复通常不允许插入null


    Map 接口

    • Map不是collection的子接口或者实现类。Map是一个接口。

    • Map用于保存具有“映射关系”的数据。每个Entry都持有键-值两个对象。其中,Value可能重复,但是Key不允许重复(和Set类似)。

    • Map可以有多个Value为null,但是只能有一个Key为null。

    • Map的子类、子接口

    • 1) HashMap (类)

      和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准一样: 两个key通过equals()方法比较返回true、 同时两个key的hashCode值也必须相等

      • 1.1) LinkedHashMap (类)
      • LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序区分)。


    • 2) HashTable (类)

      是一个古老的Map实现类。

      • 2.1) Properties(类)
      • Properties对象在处理属性文件时特别方便(windows平台的.ini文件)。Properties类可以把Map对象和属性文件关联,从而把Map对象的key - value对写入到属性文件中,也可把属性文件中的“属性名-属性值”加载进Map对象中。

    • 3) SortedMap(接口)

      如同Set->SortedSet->TreeSet一样,Map也有Map->SortedMap->TreeMap的继承关系。

      • 3.1) TreeMap(类)
      • TreeMap是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。 同时,TreeMap也有两种排序方式:自然排序、定制排序(类似于上面List的重写CompareTo()方法)。
    • 4) WeakHashMap(类)
      • 看名字就能发现,这是Weak后的HashMap。但是二者大体差别不大。
      • 区别在于,HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。
      • 但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所对应的key-value对
    • 5) IdentityHashMap(类)
      • 这个类也和HashMap类似(怎么那么多类似的hhhh),区别在于,在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等
    • 6) EnumMap(类)
      • EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的自然顺序存储。


    展开全文
  • java容器超详细

    千次阅读 多人点赞 2020-04-28 15:45:46
    java容器是前人为我们提供的一套用于存储数据和对象的工具。如果你学过C++的STL,可以与之类比。java容器又可以称为Java Collection Framework(JCF)。里面除了存储对象的容器之外,还提供了一套用于处理和操作容器...

    前言

    java容器是前人为我们提供的一套用于存储数据和对象的工具。如果你学过C++的STL,可以与之类比。java容器又可以称为Java Collection Framework(JCF)。里面除了存储对象的容器之外,还提供了一套用于处理和操作容器里面的对象的一套工具类。
    整体框架:
    在这里插入图片描述
    下面将介绍List、Set、Map以及工具类Collections和Arrays。

    List

    在这里插入图片描述

    List:列表,是一个接口。它的实现类常用的有LinkedList、ArrayList和Vector。

    LinkedList

    • LinkedList采用双向链表实现的列表,因此可以被用作队列、堆栈、双端队列;顺序访问高效,随机访问性能较差、适用于需要经常添加和删除的数据。
    • LinkedList不支持同步
    package 常用数据结构;
    
    import java.util.Iterator;
    import java.util.LinkedList;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class LinkedListTest {
        public static void main(String[] args) {
            LinkedList<Integer> linked = new LinkedList<>();
            for(int i=0;i<10;++i){
                linked.add(i);
            }
            System.out.println(linked.size());
            linked.addFirst(10);
            linked.add(3,20);
            linked.remove(3);
            LinkedList<Integer> list = new LinkedList<>();
            for(int i=0;i<100000;++i)
                list.add(i);
            traverseByIterator(list);
            traverseByIndex(list);
            traverseByFor(list);
        }
    
        private static void traverseByFor(LinkedList<Integer> list) {
            long start = System.nanoTime();
            for(Integer i:list)
                ;
            long end = System.nanoTime();
            System.out.println("for-each遍历:"+(end-start)+"ms");
        }
    
        private static void traverseByIndex(LinkedList<Integer> list) {
            long start = System.nanoTime();
            for(int i=0;i<list.size();++i)
                list.get(i);
            long end = System.nanoTime();
            System.out.println("随机索引遍历:"+(end-start)+"ms");
        }
    
        private static void traverseByIterator(LinkedList<Integer> list) {
            long start = System.nanoTime();
            Iterator<Integer> iterator = list.iterator();
            while (iterator.hasNext())
                iterator.next();
            long end = System.nanoTime();
            System.out.println("迭代器遍历:"+(end-start)+"ms");
        }
    }
    

    运行结果:
    在这里插入图片描述

    ArrayList

    • ArrayList是采用数组实现的列表,因此它支持随机访问,不适合频繁删除和插入操作。对于需要经常进行查询的数据建议采用此结构。
    • ArrayList与java数组的一个大的区别是ArrayList能够自动扩容
    • ArrayList不支持同步
    package 常用数据结构;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class ArrayListTest {
        public static void main(String[] args) {
            ArrayList<Integer> arrayList = new ArrayList<>();
            for(int i=0;i<5;++i){
                arrayList.add(i);   //这里会自动装箱
            }
            arrayList.add(0); //允许有相同的元素
            arrayList.remove(2);
            arrayList.add(3,9);
            //遍历测试
            ArrayList<Integer> arr1 = new ArrayList<>();
            for(int i=0;i<100000;++i){
                arr1.add(i);
            }
            traverseByIterator(arr1);
            traverseByIndex(arr1);
            traverseByFor(arr1);
        }
    
        private static void traverseByFor(ArrayList<Integer> arr1) {
            long start = System.nanoTime();
            for(Integer i:arr1)
                ;
            long end = System.nanoTime();
            System.out.println("for遍历:"+(end-start)+"ms");
        }
    
        private static void traverseByIndex(ArrayList<Integer> arr1) {
            long start = System.nanoTime();
            for(int i=0;i<arr1.size();++i){
                arr1.get(i);
            }
            long end = System.nanoTime();
            System.out.println("随机索引遍历:"+(end-start)+"ms");
        }
    
        private static void traverseByIterator(ArrayList<Integer> arr1) {
            long start = System.nanoTime();
            Iterator<Integer> iterator = arr1.iterator();
            while (iterator.hasNext())
                iterator.next();
            long end = System.nanoTime();
            System.out.println("迭代器遍历:"+(end-start)+"ms");
        }
    }
    

    运行结果:
    在这里插入图片描述

    Vector

    Vector用法和ArrayList用法很相似,它们的区别在于Vector是线程同步的而且Vector有另外的遍历方式。这里将不再赘述。

    总结

    对于不同的数据我们应该根据其特点采用不同的list,而不是一中list用到底。要学会灵活使用。

    Set

    Set:集合,和数学中的集合类似。
    特点:

    1. 确定性:对任一对象都能判定它是否属于某一个集和
    2. 互异性:一个集和中不会存在两个相同(内容相同)的对象
    3. 无序性:集和里面的元素没有顺序
      在这里插入图片描述
      HashSet、LinkedHashSet、TreeSet里面存放的都要是对象,不能是基本数据类型。

    HashSet

    基于散列函数的集和,采用HashMap实现,可以容纳null元素,不支持同步(可以通过Collections.synchronizedSet(new HashSet<…>()来使它同步)

    package 常用数据结构;
    
    import java.util.HashSet;
    import java.util.Iterator;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class HashSetTest {
        public static void main(String[] args) {
            HashSet<Integer> hs = new HashSet<>();
            for(int i=0;i<5;++i)
                hs.add(i);
            hs.add(1);  //无效的语句,因为集合中已经有1了。
            hs.add(null); //可以容纳null元素
            /**
             * 测试两种遍历
             */
            HashSet<Integer> hs1 = new HashSet<>();
            for(int i=0;i<100000;++i)
                hs1.add(i);
            long start = System.nanoTime();
            Iterator<Integer> iterator = hs1.iterator();
            while (iterator.hasNext())
                iterator.next();
            long end = System.nanoTime();
            System.out.println("迭代器遍历:"+(end-start)+"ms");
            start = System.nanoTime();
            for(Integer item:hs1)
                ;
            end = System.nanoTime();
            System.out.println("for-each遍历:"+(end-start)+"ms");
        }
    }
    

    运行结果:
    在这里插入图片描述

    HashLinkedSet

    继承HashSet ,基于散列函数和双向链表的集和,可以容纳null元素,通过双向链表维护了插入顺序,从而支持排序,但不支持同步(可以通过Collections.synchronizedSet(new HashSet<…>()来使它同步)

    代码和上面类似。把HashSet改成LinkedHashSet即可
    

    运行结果:
    在这里插入图片描述

    TreeSet

    基于树结构的集和,印次支持排序,不能容纳null元素,同样不支持同步

    package 常用数据结构;
    
    import java.util.TreeSet;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class TreeSetTest {
        public static void main(String[] args) {
            TreeSet<Integer> ts = new TreeSet<>();
            for(int i=0;i<10;++i)
                ts.add(i);
            ts.add(100);
            ts.add(20);
            ts.add(45);
            for(Integer item:ts)
                System.out.println(item);
    //        ts.add(null); //错误,不支持添加null元素
            ts.add(1); //重复
        }
    }
    
    

    在这里插入图片描述

    总结

    • 判定是否是重复元素的原则
      • HashSet、LinkedHashSet
      1. 判断两个对象的hashcode()是否相同,如果不同,返回flase
      2. 如果hashcode()相同,则调用equals()方法判断内容是否相同。相同返回true,不同返回flase
    package 常用数据结构;
    
    import java.util.HashSet;
    import java.util.LinkedHashSet;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class HashSetEqualsRuleTest {
        public static void main(String[] args) {
    
            HashSet<Cat> cats = new HashSet<>();
            cats.add(new Cat(1));
            cats.add(new Cat(2));  //这里将看成是两个对象,因为hashcode方法返回的值不一样
            cats.add(new Cat(2));
            System.out.println(cats.size());  //3
            //重写了hashcode()和equals()
            HashSet<Dog> dogs = new HashSet<>();
            dogs.add(new Dog(1));
            dogs.add(new Dog(2)); //这里是一个对象,因为重写了它的hashcode方法使得返回值一样
            dogs.add(new Dog(2));
            System.out.println(dogs.size());  //2
    
            LinkedHashSet<Cat> cats1 = new LinkedHashSet<>();
            cats1.add(new Cat(1));
            cats1.add(new Cat(2));
            cats1.add(new Cat(2));
            System.out.println(cats1.size());  //3
            //重写了hashcode()和equals()
            LinkedHashSet<Dog> dogs1 = new LinkedHashSet<>();
            dogs1.add(new Dog(1));
            dogs1.add(new Dog(2));
            dogs1.add(new Dog(2));
            System.out.println(dogs1.size());  //2
    
        }
    }
    class Cat{
        public int size;
    
        public Cat(int size) {
            this.size = size;
        }
    }
    class Dog {
        public  int size;
    
        public Dog(int size) {
            this.size = size;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Dog dog = (Dog) o;
            return size == dog.size;
        }
    
        @Override
        public int hashCode() {
            return size;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "size=" + size +
                    '}';
        }
    }
    
    
    • TreeSet
      添加到TreeSet里面的元素都必须实现comparable接口。因为在添加元素时会调用接口里面的compareTo()方法来比较是否是同一个元素。
    package 常用数据结构;
    
    import java.util.TreeSet;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class TreeSetEqualsRuleTet {
        public static void main(String[] args) {
    //        TreeSet<Cat> cats = new TreeSet<>();
    //        cats.add(new Cat(1));  //会报错,因为没有实现Compareable接口
    //        cats.add(new Cat(1));
    //        System.out.println(cats.size());
            TreeSet<Tigger> tiggers = new TreeSet<>();
            tiggers.add(new Tigger(1));
            tiggers.add(new Tigger(2));
            tiggers.add(new Tigger(1));
            System.out.println(tiggers.size());
        }
    }
    class Tigger implements Comparable{
        public int size;
    
        public Tigger(int size) {
            this.size = size;
        }
    
        public int getSize() {
            return size;
        }
    
        @Override
        public int compareTo(Object o) {
            System.out.println("in Tigger compareTo");
            return size-((Tigger)o).getSize();
        }
    }
    

    运行结果:
    在这里插入图片描述

    Map

    Map是一类重要的数据结构。类似于数学中的函数,key对应自变量x、value对应因变量y、散列函数对用f
    在这里插入图片描述

    Hashtable

    key和value都不能为空,HashTable是线程安全的、同步的,但只适合用于小数据量

    package 常用数据结构;
    
    import java.util.Hashtable;
    import java.util.Iterator;
    import java.util.Map;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class HashtableTest {
        public static void main(String[] args) {
            Hashtable<Integer, String> ht = new Hashtable<>();
    //        ht.put(1,null); 报错
    //        ht.put(null,"1"); 报错
    //        ht.put(null,null); 报错
            ht.put(1,"111");
            ht.put(2,"222");
            ht.put(3,"3333");
            ht.contains("111");
            ht.containsValue("111");
            ht.containsKey(0);
            /**
             * 三种遍历方式
             */
            ht.clear();
            for(int i=0;i<100000;++i)
                ht.put(i,"aaaa");
            //第一种:Entry迭代器遍历
            long start = System.nanoTime();
            Iterator<Map.Entry<Integer, String>> iterator = ht.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Integer, String> next = iterator.next();
                next.getKey();
                next.getValue();
            }
            long end = System.nanoTime();
            System.out.println("Entry迭代器遍历:"+(end-start));
            //第二种:keySet迭代器遍历
            start=System.nanoTime();
            Iterator<Integer> iterator1 = ht.keySet().iterator();
            while (iterator1.hasNext()){
                Integer key = iterator1.next();
                String value = ht.get(key);
            }
            end = System.nanoTime();
            System.out.println("keySet迭代器遍历:"+(end-start));
        }
    }
    

    运行结果:
    在这里插入图片描述

    HashMap

    HashMap允许有null,不支持同步(支持通过Collections.synchronizedMap(new Map<…,…>() 实现同步),所以线程不安全。但可以存储大量数据

    package 常用数据结构;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class HashMapTest {
        public static void main(String[] args) {
            HashMap<Integer, String> ht = new HashMap<>();
            ht.put(1,null);
            ht.put(null,"1");
            ht.put(null,null);
            ht.put(1,"111");
            ht.put(2,"222");
            ht.put(3,"3333");
            ht.containsValue("111");
            ht.containsKey(0);
            Iterator<Integer> iterator2 = ht.keySet().iterator();
            while (iterator2.hasNext()){
                Integer key = iterator2.next();
                System.out.println(key+":"+ht.get(key));
            }
            /**
             * 三种遍历方式
             */
            ht.clear();
            for(int i=0;i<100000;++i)
                ht.put(i,"aaaa");
            //第一种:Entry迭代器遍历
            long start = System.nanoTime();
            Iterator<Map.Entry<Integer, String>> iterator = ht.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Integer, String> next = iterator.next();
                next.getKey();
                next.getValue();
            }
            long end = System.nanoTime();
            System.out.println("Entry迭代器遍历:"+(end-start));
            //第二种:keySet迭代器遍历
            start=System.nanoTime();
            Iterator<Integer> iterator1 = ht.keySet().iterator();
            while (iterator1.hasNext()){
                Integer key = iterator1.next();
                String value = ht.get(key);
            }
            end = System.nanoTime();
            System.out.println("keySet迭代器遍历:"+(end-start));
        }
    }
    
    

    运行结果:
    在这里插入图片描述

    LinkedHashMap

    基于双向链表用于维持插入顺序的HashMap,继承自HashMap

    TreeMap

    基于红黑树的Map,可以根据key的自然排序或者compareTo方法排序输出

    Properties

    继承自Hashtable。特有的方法有load()、store()等等。

    总结

    Map的种类有很多,根据实际情况选择合适的Map。

    工具类

    Arrays

    Arrays处理的对象是数组,常用的方法包括排序sort、查找binarySearch、拷贝copy等等

    package 常用数据结构;
    
    import java.util.Arrays;
    import java.util.Random;
    import java.util.stream.IntStream;
    
    /**
     * @author pony
     * @date 2020/4/28
     */
    public class ArraysTest {
        public static void main(String[] args) {
            testSort();
            testSearch();
            testFill();
            testCopy();
            testEquality();
        }
    
        private static void testEquality() {
            int[] a = new int[5];
            int[] b = new int[5];
            Arrays.fill(a,1);
            Arrays.fill(b,1);
            System.out.println(Arrays.equals(a,b));
            b[1]=3;
            System.out.println(Arrays.equals(a,b));
        }
    
        private static void testCopy() {
            Random random = new Random();
            IntStream ints = random.ints(20,1,100);
            int[] a = ints.toArray();
            for(int i:a)
                System.out.println(i);
            int[] ints1 = Arrays.copyOf(a, 4);
            for(int i:ints1)
                System.out.println(i);
        }
    
        private static void testFill() {
            int[] a = new int[10];
            Arrays.fill(a,2,8,100);
            for(int i:a)
                System.out.println(i);
        }
    
        private static void testSearch() {
            Random random = new Random();
            int[] ints = random.ints(5,1,100).toArray();
            ints[ints.length-1]=100;
            int i = Arrays.binarySearch(ints, 100);
            System.out.println("100 in "+i);
        }
    
        private static void testSort() {
            Random random = new Random();
            IntStream ints = random.ints(20,1,100);
            int[] a = ints.toArray();
            System.out.println("排序前:");
            for(int i:a)
                System.out.println(i);
            Arrays.sort(a);
            System.out.println("排序后:");
            for(int i:a)
                System.out.println(i);
        }
    }
    
    

    Collections

    Collections可以操作collections接口及其所有子类、常用的用法和Arrays差不多。但它的sort方法要求被排序对象实现了compareable接口或者传入一个compactor对象(主要针对某些类不能去被修改)

    总结

    Arrays和Collections着两个工具类能够帮我们做很多事情,所以要熟练的应用。避免重复造轮子。

    总结

    写到这,容器的基本知识应该都了解的差不多了,但这只能算是对容器入门了。只是学会了如何去使用。建议以后多多去查阅API和源码。了解其内部是如何实现这些结构的。当你能够自己写出来这些容器的实现时,才算真正的掌握了java容器。

    展开全文
  • JAVA中关于”容器“的理解

    万次阅读 2018-03-10 12:00:06
    众所周知,JAVA是一门强类型的编程语言,该语言有很多的特性,而本文所要讲的是关于JAVA中容器”这一知识点的简要探究。在我看来,“容器”其实就是一种用来存储数据的数据结构,在JAVA中容器可分为即“集合”...
  • Java 集合容器篇面试题(上)-王者笔记《收藏版》

    万次阅读 多人点赞 2021-07-29 22:54:35
    前期推荐阅读: Java基础知识学习总结(上) Java 基础知识学习总结(下) ...一、集合容器概述 ...在面向对象编程语言,接口通常用来形成规范。 实现:集合接口的具体实现,是重用性很高的数据结构。 ...
  • 应用介绍 本应用为一个接口服务,旨在对外提供RestfullAPI...一、在Docker容器中,采用tar zvxf jdk1.8XXX.gz的方法,再docker commit生成镜像,再run镜像,在新容器中Java环境失效! 二、容器与宿主机共用Java...
  • java容器都有哪些?

    万次阅读 2019-09-03 20:50:29
    java容器类类库的用途是"保存对象"。摘自: “Thinking in Java”. Java集合类是一种特别有用的工具类,可以用于存储数量不等的对象,并可以实现常用的数据结构,如栈,队列等.Java集合就像一种容器,可以把多个对象(实际...
  • JAVA面试之容器

    千次阅读 2017-03-07 16:02:14
    java collections框架大量集合接口以及这些接口的实现类和操作他们的算法,具体而言,主要提供了List、Queue、Set、Stack和Map。 其中Collection接口下有List、Queue、Set、Stack。Map里面HashMap、Hashtable、...
  • 本文主要讲解Swing程序设计中容器、面板及四大布局管理器的详细使用、包括实例程序讲解、使用注意及使用技巧分享、敬请阅读! 目录 什么是容器? 什么是面板? JPanel面板 JScrollPane面板 什么是布局管理器...
  • Java并发 - 同步容器

    千次阅读 2021-05-30 13:47:07
    虽然很多人都对同步容器的性能低有偏见,但它也不是一无是处,在这里我们插播一条阿里巴巴的开发手册规范: 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法...
  • Docker 是一个轻量级容器技术,支持将软件编译成一个镜像 (image),在这个镜像里做好对软件的各种配置,然后发布这个镜像,使用者可以使用这个镜像,运行的镜像称为容器容器的启动速度是非常快的 (秒级别)。容器...
  • 组件又称控制组件,是图形用户界面不可再分的最小元素,起功能是与用户完成一次交互操作; 容器是若干个 组件和容器的集合; 容器又分为顶层容器和中间容器; 顶层容器是应用程序必须创建的一个容器,只有在创建...
  • Java常见的容器类及其区别

    万次阅读 多人点赞 2018-06-29 12:07:49
    而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么...
  • 使用方法:JPanel可以为...在大多数Java GUI外观体验(look and feel),面板容器默认是不透明。不透明的面板容器跟普通的目录窗格功能差别不大,并且可以有效帮助样式改进。设置图层管理器和其他容器(container)一...
  • 为了能够适应容器云平台的管理模式和管理理念,应用系统需要...本文针对传统的Java 应用,对如何将应用进行容器化改造和迁移到Kubernetes 平台上进行说明。 要将传统Java 应用改造迁移到Kubernetes 平台上运行,...
  • 总结 : 十分钟快速理解Java容器

    千次阅读 2018-03-30 13:43:23
    首先看一下Java容器的概念 容器可以管理对象的生命周期、对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称、如何产生(Prototype 方式或Singleton 方式)、哪个对象产生...
  • Java Spring IoC容器示例

    万次阅读 2020-05-29 14:13:14
    IoC容器是Spring的一个框架,用于管理POJO(普通的旧Java对象)的生命周期,并在需要时将其插入Java程序。 通常,Java对象通过两种方式声明其依赖项: 将它们作为参数传递给构造函数 通过将它们作为对象的 ...
  • 最近在和阿里的一些同事谈起使用Docker部署Java应用的场景,其中一个大家普遍关心的问题就是如何设置容器中JVM的内存限制。如果使用官方的Java镜像,或者基于Java镜像构建的Docker镜像,都可以通过传递JAVA_OPTS环境...
  • 14个Java并发容器,你用过几个?

    万次阅读 2020-10-24 00:30:32
    上面提到的线程安全容器都在 java.util.concurrent 包下,这个包下并发容器不少,今天全部翻出来鼓捣一下。 仅做简单介绍,后续再分别深入探索。 ConcurrentHashMap:并发版 HashMap CopyOnWr
  • 阿里面试失败后,一气之下我图解了Java中18把锁

    万次阅读 多人点赞 2021-06-17 23:21:47
    目录 乐观锁和悲观锁 独占锁和共享锁 互斥锁和读写锁 公平锁和非公平锁 ...悲观锁对应于生活悲观的人,悲观的人总是想着...回到代码世界,一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程.
  • Java容器框架(一)--概述篇

    千次阅读 2018-09-18 21:58:46
    于是趁此闲暇之际,对Java容器进行一个整体的描述,一方面是为了对Java容器能有一个整体的思维,另一方面也是为了在平常工作能够通过不同的场景对容器类的使用做到游刃有余。 我们知道Java容器类基本上都是在java...
  • 基于docker容器搭建java应用运行环境

    千次阅读 2017-08-08 09:10:28
    技术之家 ...>>文章浏览:基于docker容器搭建java应用...本文会对虚拟化技术与 Docker 容器技术做一个对比,然后引出一些 Docker 的名词术语,比如:容器、镜像等,随后将使用 Docker 搭建一个 Java Web 运行环境
  • 巧妙解析JAVA中容器的概念

    千次阅读 2015-05-26 17:00:40
    Spring 提供容器功能,容器可以管理对象的生命周期、对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称、如何产生(Prototype 方式或Singleton 方式)、哪个对象产生之后必须...
  • Java集合】之同步容器

    千次阅读 2020-07-04 21:11:45
    Java中,同步容器主要包括2类: Vector、Stack、HashTable Collections类提供的静态工厂方法创建的类 我们以相对简单的Vecotr来举例,我们先来看下Vector几个重要方法的源码: public synchroniz
  • java中一些容器底层的数据结构解析

    千次阅读 2016-05-08 23:52:34
    先来看一个java里一些主要容器的继承图:   然后分别解析一下上面几种容器底层的数据结构以及一些实现: 1.ArrayList(非线程安全的) 底层的数据结构其实就是数组,但是它比数组优秀的地方在于他是动态的,即...
  • 废话就不多了,博主默认大家都已经知道什么是LRU算法了,且都知道了JDK是有一个LinkedHashMap容器,可以稍加继承改造下就很容易实现一个LRU机制的缓存容器的; 本篇的重点其实不在jdk自带的LinkedHashMap容器上...
  • Java中数组和集合容器的剖析

    千次阅读 2015-09-17 16:03:38
    java中常用的存储容器就是数组的集合,每种容器存储的形式和结构又有所不同。 数组,是最基础的容器,在创建数组的时候有三种方式分别如下: int[] arr = new int[5]; int[] arr = new String[]{1,2,3,4,5}...
  • Java基础入门笔记-链表与容器

    千次阅读 2018-01-07 16:11:39
    容器是一个设计上的术语,不是一个语法概念。 比如数组,就是一个容器 数组容器 缺点: 1.容量固定,无法扩展。既不能射太大,也不能太小,很难确定。 2.插入和删除代价大。 链表可以完美解决。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 364,583
精华内容 145,833
关键字:

下面不是java中的容器

java 订阅