你的位置:首页 > 软件开发 > Java > Java集合总览

Java集合总览

发布时间:2017-09-20 20:00:10
一、集合与数组的区别:  数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。  集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。二、关系图:  图中,实线边框的是实现类,折线边框的是抽象类,而点线边 ...

Java集合总览

一、集合与数组的区别:

  数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。

  集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。

二、关系图:

  图中,实线边框的是实现类,折线边框的是抽象类,而点线边框的是接口

  Java集合总览

  Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。

  Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。

  Iterator,所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
    1.hasNext()是否还有下一个元素。
    2.next()返回下一个元素。
    3.remove()删除当前元素。

三、几种接口和类的简介:

  1、List(有序、可重复)

    List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

  2、Set(无序、不能重复)
    Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

  3、Map(键值对、键唯一、值不唯一)
    Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。

    对比如下:

 

 

是否有序

是否允许元素重复

Collection

List

Set

AbstractSet

 

HashSet

 

TreeSet

是(用二叉排序树)

Map

AbstractMap

使用key-value来映射和存储数据,key必须唯一,value可以重复

 

HashMap

 

TreeMap

是(用二叉排序树)

四、遍历: 

  在类集中提供了以下四种的常见输出方式:

  1)Iterator:迭代输出,是使用最多的输出方式。

  2)ListIterator:是Iterator的子接口,专门用于输出List中的内容。

  3)foreach输出:JDK1.5之后提供的新功能,可以输出数组或集合。

  4)for循环,代码示例如下:

 for的形式:for(int i=0;i<arr.size();i++){...}
foreach的形式: for(int i:arr){...}
 iterator的形式:  Iterator it = arr.iterator();  while(it.hasNext()){ object o =it.next(); ...}

五、ArrayList和LinkedList

  ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。LinkedList经常用在增删操作较多而查询操作很少的情况下,ArrayList则相反。

六、Map集合;

  实现类:HashMap、Hashtable、LinkedHashMap和TreeMap

  HashMap :是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的

  Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

  ConcurrentHashMap:线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

  LinkedHashMap:LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

  TreeMap:TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;

 

  map的遍历

   第一种:KeySet()    

    将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 。取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。典型用法如下:

Map map = new HashMap();map.put("key1","lisi1");map.put("key2","lisi2");map.put("key3","lisi3");map.put("key4","lisi4"); //先获取map集合的所有键的set集合,keyset()Iterator it = map.keySet().iterator(); //获取迭代器while(it.hasNext()){  Object key = it.next();  System.out.println(map.get(key));}

  第二种:entrySet()
    Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取key和value。返回的是Entry接口。典型用法如下:

Map map = new HashMap();map.put("key1","lisi1");map.put("key2","lisi2");map.put("key3","lisi3");map.put("key4","lisi4");//将map集合中的映射关系取出,存入到set集合Iterator it = map.entrySet().iterator();while(it.hasNext()){  Entry e =(Entry) it.next();  System.out.println("键"+e.getKey () + "的值为" + e.getValue());}

  推荐使用第二种方式,即entrySet()方法,效率较高。对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。

 

七:集合详解; 

  1、Iterator:迭代器,它是Java集合的顶层接口(不包括 map 系列的集合,Map接口 是 map 系列集合的顶层接口)

    Object next():返回迭代器刚越过的元素的引用,返回值是 Object,需要强制转换成自己需要的类型

    boolean hasNext():判断容器内是否还有可供访问的元素

    void remove():删除迭代器刚越过的元素

    所以除了 map 系列的集合,我们都能通过迭代器来对集合中的元素进行遍历。

    注意:我们可以在源码中追溯到集合的顶层接口,比如 Collection 接口,可以看到它继承的是类 Iterable

    Java集合总览

  说明一下 Iterator 和 Iterable 的区别:

   Iterable :存在于 java.lang 包中。

    Java集合总览

  里面封装了 Iterator 接口。所以只要实现了只要实现了Iterable接口的类,就可以使用Iterator迭代器了。

  

   Iterator :存在于 java.util 包中。核心的方法next(),hasnext(),remove()。

  引用一个Iterator 的实现类 ArrayList 来看一下迭代器的使用:暂时先不管 List 集合是什么,只需要看看迭代器的用法就行了

     //产生一个 List 集合,典型实现为 ArrayList。  List list = new ArrayList();  //添加三个元素  list.add("Tom");  list.add("Bob");  list.add("Marry");  //构造 List 的迭代器  Iterator it = list.iterator();  //通过迭代器遍历元素  while(it.hasNext()){   Object obj = it.next();   System.out.println(obj);  }

 

  2、Collection:List 接口和 Set 接口的父接口Java集合总览

    

//我们这里将 ArrayList集合作为 Collection 的实现类  Collection collection = new ArrayList();    //添加元素  collection.add("Tom");  collection.add("Bob");    //删除指定元素  collection.remove("Tom");    //删除所有元素  Collection c = new ArrayList();  c.add("Bob");  collection.removeAll(c);    //检测是否存在某个元素  collection.contains("Tom");    //判断是否为空  collection.isEmpty();    //利用增强for循环遍历集合  for(Object obj : collection){   System.out.println(obj);  }  //利用迭代器 Iterator  Iterator iterator = collection.iterator();  while(iterator.hasNext()){   Object obj = iterator.next();   System.out.println(obj);  }

 

  3、List :有序,可以重复的集合。Java集合总览   

    由于 List 接口是继承于 Collection 接口,所以基本的方法如上所示。

   1)、List 接口的三个典型实现:

     ●List list1 = new ArrayList();

      底层数据结构是数组,查询快,增删慢;线程不安全,效率高

     ●List list2 = new Vector();

      底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合

     ●List list3=LinkedList();

      底层数据结构是链表,查询慢,增删快;线程不安全,效率高

   2)、List 接口遍历还可以使用普通 for 循环进行遍历,指定位置添加元素,替换元素等等。

    

    //产生一个 List 集合,典型实现为 ArrayList  List list = new ArrayList();  //添加三个元素  list.add("Tom");  list.add("Bob");  list.add("Marry");  //构造 List 的迭代器  Iterator it = list.iterator();  //通过迭代器遍历元素  while(it.hasNext()){   Object obj = it.next();   //System.out.println(obj);  }    //在指定地方添加元素  list.add(2, 0);     //在指定地方替换元素  list.set(2, 1);     //获得指定对象的索引  int i=list.indexOf(1);  System.out.println("索引为:"+i);    //遍历:普通for循环  for(int j=0;j<list.size();j++){    System.out.println(list.get(j));  }

  

  4、Set:典型实现 HashSet()是一个无序,不可重复的集合

    1)、Set hashSet = new HashSet();

    • HashSet:不能保证元素的顺序;不可重复;不是线程安全的;集合元素可以为 NULL;
    •  其底层其实是一个数组,存在的意义是加快查询速度。我们知道在一般的数组中,元素在数组中的索引位置是随机的,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定的值时,需要把查找值和一系列的元素进行比较,此时的查询效率依赖于查找过程中比较的次数。而 HashSet 集合底层数组的索引和值有一个确定的关系:index=hash(value),那么只需要调用这个公式,就能快速的找到元素或者索引。
    • 对于 HashSet: 如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。
    •  当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置
    • 如果 hashCode 值不同,直接把该元素存储到 hashCode() 指定的位置
    • 如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals() 作比较
    • hashCode 相同,equals 为 true,则视为同一个对象,不保存在 hashSet()中
    • hashCode 相同,equals 为 false,则存储在之前对象同槽位的链表上,这非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

    

    2)、Set linkedHashSet = new LinkedHashSet();
    •  不可以重复,有序,因为底层采用 链表 和 哈希表的算法。链表保证元素的添加顺序,哈希表保证元素的唯一性

    3)、Set treeSet = new TreeSet(); 

    •  TreeSet:有序;不可重复,底层使用 红黑树算法,擅长于范围查询。
    •  如果使用 TreeSet() 无参数的构造器创建一个 TreeSet 对象, 则要求放入其中的元素的类必须实现 Comparable 接口所以, 在其中不能放入 null 元素
    •  必须放入同样类的对象.(默认会进行排序) 否则可能会发生类型转换异常.我们可以使用泛型来进行限制
     Set treeSet = new TreeSet();  treeSet.add(1); //添加一个 Integer 类型的数据  treeSet.add("a"); //添加一个 String 类型的数据  System.out.println(treeSet); //会报类型转换异常的错误
    • 自动排序:添加自定义对象的时候,必须要实现 Comparable 接口,并要覆盖 compareTo(Object obj) 方法来自定义比较规则

      如果 this > obj,返回正数 1

      如果 this < obj,返回负数 -1

      如果 this = obj,返回 0 ,则认为这两个对象相等

    • 两个对象通过 Comparable 接口 compareTo(Object obj) 方法的返回值来比较大小, 并进行升序排列

    Java集合总览

    •  定制排序: 创建 TreeSet 对象时, 传入 Comparator 接口的实现类. 要求: Comparator 接口的 compare 方法的返回值和 两个元素的 equals() 方法具有一致的返回值  
public class TreeSetTest { public static void main(String[] args) {  Person p1 = new Person(1);  Person p2 = new Person(2);  Person p3 = new Person(3);     Set<Person> set = new TreeSet<>(new Person());  set.add(p1);  set.add(p2);  set.add(p3);  System.out.println(set); //结果为[1, 2, 3] } } class Person implements Comparator<Person>{ public int age; public Person(){} public Person(int age){  this.age = age; } @Override /***  * 根据年龄大小进行排序  */ public int compare(Person o1, Person o2) {  // TODO Auto-generated method stub  if(o1.age > o2.age){   return 1;  }else if(o1.age < o2.age){   return -1;  }else{   return 0;  } }   @Override public String toString() {  // TODO Auto-generated method stub  return ""+this.age; }}

  当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果

  以上三个 Set 接口的实现类比较:  共同点:1、都不允许元素重复      2、都不是线程安全的类,解决办法:Set set = Collections.synchronizedSet(set 对象)    不同点:    HashSet:不保证元素的添加顺序,底层采用 哈希表算法,查询效率高。判断两个元素是否相等,equals() 方法返回 true,hashCode() 值相等。即要求存入 HashSet 中的元素要覆盖 equals() 方法和 hashCode()方法     LinkedHashSet:HashSet 的子类,底层采用了 哈希表算法以及 链表算法,既保证了元素的添加顺序,也保证了查询效率。但是整体性能要低于 HashSet         TreeSet:不保证元素的添加顺序,但是会对集合中的元素进行排序。底层采用 红-黑 树算法(树结构比较适合范围查询)

 

  5、Map:key-value 的键值对,key 不允许重复,value 可以

    严格来说 Map 并不是一个集合,而是两个集合之间 的映射关系。

    这两个集合没每一条数据通过映射关系,我们可以看成是一条数据。即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。

    因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。

    Java集合总览

    

Map<String,Object> hashMap = new HashMap<>();  //添加元素到 Map 中  hashMap.put("key1", "value1");  hashMap.put("key2", "value2");  hashMap.put("key3", "value3");  hashMap.put("key4", "value4");  hashMap.put("key5", "value5");     //删除 Map 中的元素,通过 key 的值  hashMap.remove("key1");     //通过 get(key) 得到 Map 中的value  Object str1 = hashMap.get("key1");     //可以通过 添加 方法来修改 Map 中的元素  hashMap.put("key2", "修改 key2 的 Value");     //通过 map.values() 方法得到 Map 中的 value 集合  Collection<Object> value = hashMap.values();  for(Object obj : value){   //System.out.println(obj);  }     //通过 map.keySet() 得到 Map 的key 的集合,然后 通过 get(key) 得到 Value  Set<String> set = hashMap.keySet();  for(String str : set){   Object obj = hashMap.get(str);   //System.out.println(str+"="+obj);  }     //通过 Map.entrySet() 得到 Map 的 Entry集合,然后遍历  Set<Map.Entry<String, Object>> entrys = hashMap.entrySet();  for(Map.Entry<String, Object> entry: entrys){   String key = entry.getKey();   Object value2 = entry.getValue();   System.out.println(key+"="+value2);  }     System.out.println(hashMap);

 

  6、Map 和 Set 集合的关系

  都有几个类型的集合。HashMap 和 HashSet ,都采 哈希表算法;TreeMap 和 TreeSet 都采用 红-黑树算法;LinkedHashMap 和 LinkedHashSet 都采用 哈希表算法和红-黑树算法。

  分析 Set 的底层源码,我们可以看到,Set 集合 就是 由 Map 集合的 Key 组成。

  Java集合总览

 

    

 

   以下总结所有的Java集合(Collection)。主要介绍各个集合的特性和用途,以及在不同的集合类型之间转换的方式。

Arrays

  Array是Java特有的数组。在你知道所要处理数据元素个数的情况下非常好用。java.util.Arrays包含了许多处理数据的实用方法:  

  • Arrays.asList:可以从 Array 转换成 List。可以作为其他集合类型构造器的参数。
  • Arrays.binarySearch:在一个已排序的或者其中一段中快速查找。
  • Arrays.copyOf:如果你想扩大数组容量又不想改变它的内容的时候可以使用这个方法。
  • Arrays.copyOfRange:可以复制整个数组或其中的一部分。
  • Arrays.deepEqualsArrays.deepHashCode:Arrays.equals/hashCode的高级版本,支持子数组的操作。
  • Arrays.equals:如果你想要比较两个数组是否相等,应该调用这个方法而不是数组对象中的 equals方法(数组对象中没有重写equals()方法,所以这个方法之比较引用而不比较内容)。这个方法集合了Java 5的自动装箱和无参变量的特性,来实现将一个变量快速地传给 equals() 方法——所以这个方法在比较了对象的类型之后是直接传值进去比较的。
  • Arrays.fill:用一个给定的值填充整个数组或其中的一部分。
  • Arrays.hashCode:用来根据数组的内容计算其哈希值(数组对象的hashCode()不可用)。这个方法集合了Java 5的自动装箱和无参变量的特性,来实现将一个变量快速地传给 Arrays.hashcode方法——只是传值进去,不是对象。
  • Arrays.sort:对整个数组或者数组的一部分进行排序。也可以使用此方法用给定的比较器对对象数组进行排序。
  • Arrays.toString:打印数组的内容。

  注释: 如果想要复制整个数组或其中一部分到另一个数组,可以调用 System.arraycopy方法。此方法从源数组中指定的位置复制指定个数的元素到目标数组里。这无疑是一个简便的方法。有时候用 ByteBuffer bulk复制会更快。所有的集合都可以用T[] Collection.toArray( T[] a ) 这个方法复制到数组中。通常会用这样的方式调用:

1 return coll.toArray( new T[ coll.size() ] );

这个方**分配足够大的数组来储存所有的集合,这样 toArray 在返回值时就不必再分配空间了。

 

单线程集合

  这一部分介绍的是不支持多线程的集合。这些集合都在java.util包里。

  

List

  • ArrayList:最有用的List集合实现。由一个整形数字或数组存储了集合的大小(数组中第一个没有使用的元素)。像所有的List集合一样,ArrayList可以在必要的时候扩展它的大小。ArrayList访问元素的时间开销固定。在尾部添加元素成本低(为常数复杂度),而在头部添加元素成本很高(线性复杂度)。这是由ArrayList的实现原理——所有的元素的从角标为0开始一个接着一个排列造成的。也就是说,从要插入的元素位置往后,每个元素都要向后移动一个位置。CPU缓存友好的集合是基于数组的。(其实也不是很友好,因为有时数组会包含对象,这样存储的只是指向实际对象的指针)。
  • LinkedList:Deque实现:每一个节点都保存着上一个节点和下一个节点的指针。这就意味着数据的存取和更新具有线性复杂度(这也是一个最佳化的实现,每次操作都不会遍历数组一半以上,操作成本最高的元素就是数组中间的那个)。如果想写出高效的LinkedList代码可以使用 ListIterators 。如果你想用一个Queue/Deque实现的话(你只需读取第一个和最后一个元素就行了)——考虑用ArrayDeque代替。
  • Vector:一个带有线程同步方法的ArrayList版本。现在直接用ArrayList代替了。

Queues/deques

  • ArrayDeque:Deque是基于有首尾指针的数组(环形缓冲区)实现的。和LinkedList不同,这个类没有实现List接口。因此,如果没有首尾元素的话就不能取出任何元素。这个类比LinkedList要好一些,因为它产生的垃圾数量较少(在扩展的时候旧的数组会被丢弃)。
  • Stack:一种后进先出的队列。不要在生产代码中使用,使用别的Deque来代替(ArrayDeque比较好)。
  • PriorityQueue:一个基于优先级的队列。使用自然顺序或者制定的比较器来排序。他的主要属性——poll/peek/remove/element会返回一个队列的最小值。不仅如此,PriorityQueue还实现了Iterable接口,队列迭代时不进行排序(或者其他顺序)。在需要排序的集合中,使用这个队列会比TreeSet等其他队列要方便。

Maps

  • HashMap:最常用的Map实现。只是将一个键和值相对应,并没有其他的功能。对于复杂的hashCode methodget/put方法有固定的复杂度。
  • EnumMap:枚举类型作为键值的Map。因为键的数量相对固定,所以在内部用一个数组储存对应值。通常来说,效率要高于HashMap
  • HashTable:旧HashMap的同步版本,新的代码中也使用了HashMap
  • IdentityHashMap:这是一个特殊的Map版本,它违背了一般Map的规则:它使用 “==” 来比较引用而不是调用Object.equals来判断相等。这个特性使得此集合在遍历图表的算法中非常实用——可以方便地在IdentityHashMap中存储处理过的节点以及相关的数据。
  • LinkedHashMap :HashMapLinkedList的结合,所有元素的插入顺序存储在LinkedList中。这就是为什么迭代LinkedHashMap的条目(entry)、键和值的时候总是遵循插入的顺序。在JDK中,这是每元素消耗内存最大的集合。
  • TreeMap:一种基于已排序且带导向信息Map的红黑树。每次插入都会按照自然顺序或者给定的比较器排序。这个Map需要实现equals方法和Comparable/ComparatorcompareTo需要前后一致。这个类实现了一个NavigableMap接口:可以带有与键数量不同的入口,可以得到键的上一个或者下一个入口,可以得到另一Map某一范围的键(大致和SQLBETWEEN运算符相同),以及其他的一些方法。
  • WeakHashMap:这种Map通常用在数据缓存中。它将键存储在WeakReference中,就是说,如果没有强引用指向键对象的话,这些键就可以被垃圾回收线程回收。值被保存在强引用中。因此,你要确保没有引用从值指向键或者将值也保存在弱引用中m.put(key, new WeakReference(value))

Sets

  • HashSet:一个基于HashMapSet实现。其中,所有的值为“假值”(同一个Object对象具备和HashMap同样的性能。基于这个特性,这个数据结构会消耗更多不必要的内存。
  • EnumSet:值为枚举类型的Set。Java的每一个enum都映射成一个不同的int。这就允许使用BitSet——一个类似的集合结构,其中每一比特都映射成不同的enumEnumSet有两种实现,RegularEnumSet——由一个单独的long存储(能够存储64个枚举值,99.9%的情况下是够用的),JumboEnumSet——由long[]存储。
  • BitSet:一个比特Set。需要时常考虑用BitSet处理一组密集的整数Set(比如从一个预先知道的数字开始的id集合)。这个类用 long[]来存储bit
  • LinkedHashMap:与HashSet一样,这个类基于LinkedHashMap实现。这是唯一一个保持了插入顺序的Set
  • TreeSet:与HashSet类似。这个类是基于一个TreeMap实例的。这是在单线程部分唯一一个排序的Set

java.util.Collections

  就像有专门的java.util.Arrays来处理数组,Java中对集合也有java.util.Collections来处理。

  第一组方法主要返回集合的各种数据:

  • Collections.checkedCollection / checkedList / checkedMap / checkedSet / checkedSortedMap / checkedSortedSet:检查要添加的元素的类型并返回结果。任何尝试添加非法类型的变量都会抛出一个ClassCastException异常。这个功能可以防止在运行的时候出错。//fixme
  • Collections.emptyList / emptyMap / emptySet :返回一个固定的空集合,不能添加任何元素。
  • Collections.singleton / singletonList / singletonMap:返回一个只有一个入口的 set/list/map 集合。
  • Collections.synchronizedCollection / synchronizedList / synchronizedMap / synchronizedSet / synchronizedSortedMap / synchronizedSortedSet:获得集合的线程安全版本(多线程操作时开销低但不高效,而且不支持类似putupdate这样的复合操作)
  • Collections.unmodifiableCollection / unmodifiableList / unmodifiableMap / unmodifiableSet / unmodifiableSortedMap / unmodifiableSortedSet:返回一个不可变的集合。当一个不可变对象中包含集合的时候,可以使用此方法。

  第二组方法中,其中有一些方法因为某些原因没有加入到集合中:

  • Collections.addAll:添加一些元素或者一个数组的内容到集合中。
  • Collections.binarySearch:和数组的Arrays.binarySearch功能相同。
  • Collections.disjoint:检查两个集合是不是没有相同的元素。
  • Collections.fill:用一个指定的值代替集合中的所有元素。
  • Collections.frequency:集合中有多少元素是和给定元素相同的。
  • Collections.indexOfSubList / lastIndexOfSubList:和String.indexOf(String) / lastIndexOf(String)方法类似——找出给定的List中第一个出现或者最后一个出现的子表。
  • Collections.max / min:找出基于自然顺序或者比较器排序的集合中,最大的或者最小的元素。
  • Collections.replaceAll:将集合中的某一元素替换成另一个元素。
  • Collections.reverse:颠倒排列元素在集合中的顺序。如果你要在排序之后使用这个方法的话,在列表排序时,最好使用Collections.reverseOrder比较器。
  • Collections.rotate:根据给定的距离旋转元素。
  • Collections.shuffle:随机排放List集合中的节点,可以给定你自己的生成器——例如 java.util.Random / java.util.ThreadLocalRandom or java.security.SecureRandom
  • Collections.sort:将集合按照自然顺序或者给定的顺序排序。
  • Collections.swap:交换集合中两个元素的位置(多数开发者都是自己实现这个操作的)。

并发集合

    这一部分将介绍java.util.concurrent包中线程安全的集合。这些集合的主要属性是一个不可分割的必须执行的方法。因为并发的操作,例如addupdate或者checkupdate,都有一次以上的调用,必须同步。因为第一步从集合中组合操作查询到的信息在开始第二步操作时可能变为无效数据。

    多数的并发集合是在Java 1.5引入的。ConcurrentSkipListMap / ConcurrentSkipListSet 和 LinkedBlockingDeque是在Java 1.6新加入的。Java 1.7加入了最后的 ConcurrentLinkedDeque 和 LinkedTransferQueue

Lists

  • CopyOnWriteArrayList:list的实现每一次更新都会产生一个新的隐含数组副本,所以这个操作成本很高。通常用在遍历操作比更新操作多的集合,比如listeners/observers集合。

Queues/deques

  • ArrayBlockingQueue:基于数组实现的一个有界阻塞队,大小不能重新定义。所以当你试图向一个满的队列添加元素的时候,就会受到阻塞,直到另一个方法从队列中取出元素。
  • ConcurrentLinkedDeque / ConcurrentLinkedQueue:基于链表实现的无界队列,添加元素不会堵塞。但是这就要求这个集合的消费者工作速度至少要和生产这一样快,不然内存就会耗尽。严重依赖于CAS(compare-and-set)操作。
  • DelayQueue:无界的保存Delayed元素的集合。元素只有在延时已经过期的时候才能被取出。队列的第一个元素延期最小(包含负值——延时已经过期)。当你要实现一个延期任务的队列的时候使用(不要自己手动实现——使用ScheduledThreadPoolExecutor)。
  • LinkedBlockingDeque / LinkedBlockingQueue:可选择有界或者无界基于链表的实现。在队列为空或者满的情况下使用ReentrantLock-s
  • LinkedTransferQueue:基于链表的无界队列。除了通常的队列操作,它还有一系列的transfer方法,可以让生产者直接给等待的消费者传递信息,这样就不用将元素存储到队列中了。这是一个基于CAS操作的无锁集合。
  • PriorityBlockingQueue:PriorityQueue的无界的版本。
  • SynchronousQueue:一个有界队列,其中没有任何内存容量。这就意味着任何插入操作必须等到响应的取出操作才能执行,反之亦反。如果不需要Queue接口的话,通过Exchanger类也能完成响应的功能。

Maps

  • ConcurrentHashMap:get操作全并发访问,put操作可配置并发操作的哈希表。并发的级别可以通过构造函数concurrencyLevel参数设置(默认级别16)。该参数会在Map内部划分一些分区。在put操作的时候只有只有更新的分区是锁住的。这种Map不是代替HashMap的线程安全版本——任何 get-then-put的操作都需要在外部进行同步。
  • ConcurrentSkipListMap:基于跳跃列表(Skip List)的ConcurrentNavigableMap实现。本质上这种集合可以当做一种TreeMap的线程安全版本来使用。

Sets

  • ConcurrentSkipListSet:使用 ConcurrentSkipListMap来存储的线程安全的Set
  • CopyOnWriteArraySet:使用CopyOnWriteArrayList来存储的线程安全的Set

 

原标题:Java集合总览

关键词:JAVA

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。