Java集合总结

来源:互联网 发布:没有网络怎么重装系统 编辑:程序博客网 时间:2024/06/01 10:11

PS:

       

List集合的特有迭代器是什么---特有的迭代器是listIterator

              List和Set都有iterator()来取得其迭代器。对List来说,你还可以通过listIterator()取得其迭代器,两种迭代器在有些时候是不能通用的,Iterator和ListIterator主要区别在以下方面:

1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能。Iterator是ListIterator的父接口。

2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。

3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。其实,数组对象也可以用迭代器来实现。

org.apache.commons.collections.iterators.ArrayIterator就可以实现此功能。一般情况下,我们使用Iterator就可以了,如果你需要进行记录的前后反复检索的话,你就可以使用ListIterator来扩展你的功能,(有点象JDBC中的滚动结果集)

5  ListIterator中具备着对被遍历的元素进行增删改查的方法,可以对元素进行逆向遍历


一 前言:讲集合collection之前,我们先分清三个概念:

    colection 集合,用来表示一种数据结构(集合--逻辑结构的一种)
      

       Collection 集合接口,指的是 java.util.Collection接口,是 Set、List 和 Queue 接口的超类接口    
     

       Collections 集合工具类,指的是 java.util.Collections 类。



  Collection完整图---VS---重点学习图


  

 重点学习图:(如下)


二   分别介绍

   
  这里说的集合指的是小写的collection(集合数据结构),集合有4种基本形式,其中前三种的父接口是Collection(集合接口)

  1. List 关注事物的索引列表
  2. Set 关注事物的唯一性
  3. Queue 关注事物被处理时的顺序
  4. Map 关注事物的映射和键值的唯一性

(一) List

                   List接口:有序的列表,允许重复元素,它的有序是指假如元素时的顺序。
                  
                    1) ArrayList:  数组列表。内部采用数组结构来存储元素, 根据索引来存取元素的效率很高 
                                                           适合于:要遍历显示所有元素的情况。不适合需要频繁增删元素的情况。
                    2) LinkedList:链式列表。内部采用双向链表结构来存储元素.适合于:需要频繁增删元素的情况下。但不要使用根据索引来获取元素。
                     
                     双向链表结构不仅可以后向迭代,还可以前向迭代,并且支持元素的修改。
               
  ArrayList 可以将它理解成一个可增长的数组,它提供快速迭代和快速随机访问的能力。    LinkedList 中的元素之间是双链接的,当需要快速插入和删除时LinkedList成为List中的不二选择。
  Vector 是ArrayList的线程安全版本,性能比ArrayList要低,现在已经很少使用.
              Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
    

(二)Set 

           允许重复元素(equals相等)的集合接口。
         
            1) 要存放到Set接口中的对象,它对应的类应该重写equals方法和hashCode方法,
                 以实现业务意义上的相等规则。 
            2) 常用的数据类型:8种基本数据类型包装类、String类、Date类、Calendar类它们
                都重写了equals和hashCode方法。

          HashSet: 内部采用散列表来存储元素。

           a) 散列表的存储原理:
                 首先获取对象的哈希码值,根据这个值用固定算法计算出它在散列表中的存储位置;把对象存储 到散列表 的指定位置。存储到指定位置时,先看指定位置原来是否已经存在对象,如果存在,还需要进行equals比较(如果equals相等,说明存在重复元素,不存放,否则存放)。
          
           b) 散列表存取对象的效率最高。
            
           c) 适合情况:不允许重复元素,又不在乎存取顺序。
 
            LinkedHashSet: 内部采用散列表和双向链表结构来存储元素。 
                                
                                         适合情况:不允许重复元素,但又需要保存元素的顺序。 

          TreeSet:树集。内部使用红黑树来存储排序的元素。
             
             a) 一种方式,存入的对象对应的类实现了Comparable接口。
        
             b) 另一种方式,把一个比较器(实现Comparator接口的类)的对象传入该类的构造器来创建指定比较器的树
                                       集对象。
                                     
      注:  Comparable接口和Comparator接口
    
      a) 可排序的类必须实现java.lang.Comparable接口,在compareTo方法中定义比较规则。
         int compareTo(T t);如果当前对象小于指定对象,返回负整数;大于,返回正整数。等于,返回0。

      b) 为了便于使用各种排序规则的要求,可以通过实现Comparator<T>接口来定义比较器类。
         int compare(T t1, T t2);如果t1对象小于t2对象,返回负整数;大于,返回正整数。等于,返回0。


                
         HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此类。哈希表是通过使用称为散列法的机制来存储信息的,是专门为快速查询而设计的。存入 HashSet的对象必须定义hashCode方法。元素并没有以某种特定顺序来存放

         LinkedHashset 当不希望集合中有重复值,并且希望按照元素的插入顺序进行迭代遍历时可采用此类。内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。

         TreeSet 当不希望集合中有重复值,并且希望按照元素的自然顺序进行排序时可以采用此类。TreeSet采用红黑树的数据结构进行排序元素,使用它可以从 Set中提取有序(升序或者降序)的序列。需要注意的是,存入自定义类时,TreeSet需要维护元素的存储顺序,因此自定义类要实现Comparable接口并定义compareTo方法。若不实现Comparable接口/Comparator接口,则按自然顺序意思是某种和插入顺序无关,而是和元素本身的内容和特质有关的排序 方式,譬如“abc”排在“abd”前面

      
          
        Set 之  PS:

                1   Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。 
                     
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不 会接受这两个对象。 
                
               2   HashSet 不能保证元素的排列顺序,顺序有可能发生变化 ,不是同步的 ,集合元素可以是null,但只能放入一个null 。
                    当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在                           HashSet中存储位置。 简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相                       等 。注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法                     比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
              
              3    TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默                    认的排序方式。向TreeSet中加入的应该是同一个类的对象。 TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过                                    CompareTo方法比较没有返回0 自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现
                   int compare(T o1,T o2)方法自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法

(三)Queue(用于保存将要执行的任务列表。

       
       LinkedList 同样实现了Queue接口,可以实现先进先出的队列。
      
       PriorityQueue 用来创建自然排序的优先级队列。番外篇中有个例子http://android.yaohuiji.com/archives/3454你可以看一下
    
       PriorityQueue优先级队列相关知识之  PS:
         
                   1.一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用 的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级 队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException);
      
                   2.此队列的头 是按指定排序方式确定的最小 元素。如果多个元素都是最小值,则头是其中一个元素——选择方法是任意的。队列获取操作 poll、remove、peek 和 element 访问处于队列头的元素;
     
                   3.poll()方法:获取并移除此队列的头,如果此队列为空,则返回 null;
     
                   4.peek()方法:获取但不移除此队列的头;如果此队列为空,则返回 null。
       

(四) Map (  Map关心的是唯一的标识符。他将唯一的键映射到某个元素。当然键和值都是对象。

             1) HashMap:哈希映射(散列映射)。内部采用散列表来存储键/值对的键。

                    a) 作为映射对的键对应的类要重写equals和hashCode方法,以实现键相等规则。
                    b) 因为键是用散列表存放的,所以键/值对在Map中的存放是无序的。
                    c) 适合于:需要以键/值对形式存放元素时。它的存取效率很高。它是使用频率最高的一个集合类。

            2)LinkedHashMap:内部采用哈希表和双向链表来存储,键/值对的存放有序。
     
           3)TreeMap:树结构映射。内部使用红黑树来存储排序的键

               a) 一种方式,存入的键对应的类实现了Comparable接口。
               b) 另一种方式,把一个针对键的比较器(实现Comparator接口的类)对象传入该类的构造器来创建。

   

          HashMap 当需要键值对表示,又不关心顺序时可采用HashMap。HashMap的key为唯一,value可重复。key与value允许为null值,但key仅能有一个null值。 HashMap非线程安全  。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
                  
          Hashtable 注意Hashtable中的t是小写的,它是HashMap的线程安全版本,现在已经很少使用。而其子类Properties经常会使用 它不允许记录的键或者值为空;它支持线程的同步,是线程安全的。

          LinkedHashMap 当需要键值对,并且关心插入顺序时可采用它。LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。LinkedHashMap也是一个HashMap,但是内部维持了一个双向链表,可以保持顺序。(因为是双向链表,其除了能向后遍历,还支持向前遍历,并可在遍历过程中删除元素)

         TreeMap 当需要键值对,并关心元素的自然排序时可采用它。TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。不仅可以保持顺序,而且可以用于排序.
  
      Map之   PS: 
                一般情况下,我们用的最多的是HashMap,HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
                    TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
                    LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。

三   列表、集合与数组的互相转换

  1 List、Set转为数组

   List转换成数组可以使用List的toArray方法,返回一个Object数组。
   Set转换成数组可以使用Set的toArray方法,返回一个Object数组。
               如果List或者Set中元素的 类型 都为 A,那么可以使用带参数的toArray方法,将得到类型为A的数组,具体语句是(A[])set.toArray(new A[0]),例: ArrayList<Integer>  lt=new  ArrayList<Integer>();
                                                             int [ ] tmp={ };
                                                             int [ ] target = (int [ ] )lt.toArray(tmp); 

2 数组只能直接转化为List

    

        数组转换成List可以使用Arrays的asList静态方法,得到一个List。

                   例如:  String[] sa ={"one","two","three","four"}; 
                                List list = Arrays.asList(sa);

      数组转换成Set时,需要先将数组转换成List,再将List构造Set。 List  lt=new  HashSet( List) ; 使用Set的构造函数,函数参数为Collection类型。

                   public HashSet(Collection<? extends E> c) {  }

                   public ArrayList(Collection<? extends E> c) { }  

四  线程安全的集合类

    线程安全:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

      Vector-----ArrayLIst
      HashTable---HashMap

     Vector和Hashtable在各自的方法上都加了synchronized关键字来保持同步。

a: 
     JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。HashTable的所有方法都是同步的。
b:
     JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的      线程安全类。
c:
      JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活         性。 同时进行的读和写操作都可以并发地执行。
d:
      util.concurrent包也提供了一个线程安全的ArrayList替代者CopyOnWriteArrayList。
并发集合类,完全无需考虑并发问题。

   ConcurrentHashMap
   ConcurrentLinkedQueue
  CopyOnWriteArrayList
  CopyOnWriteArraySet

 e:ArrayList多线程下添加元素出错的原因分析:
1public boolean add(E e) {
2    ensureCapacity(size + 1);  // Increments modCount!!
3    elementData[size++] = e;
4    return true;
5    }

   在多线程的操作下导致扩容和赋值操作的不统一导致了出错。

f: 

HashMap多线程下添加元素出错的原因分析:

01public V put(K key, V value) {
02...
03addEntry(hash, key, value, i);
04...
05}
06 
07void addEntry(int hash, K key, V value, int bucketIndex) {
08    Entry<K,V> e = table[bucketIndex];
09    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
10    if (size++ >= threshold)
11       resize(2 * table.length);
12}
13 
14void resize(int newCapacity) {
15      Entry[] oldTable = table;
16      int oldCapacity = oldTable.length;
17      if (oldCapacity == MAXIMUM_CAPACITY) {
18          threshold = Integer.MAX_VALUE;
19          return;
20      }
21 
22      Entry[] newTable = new Entry[newCapacity];
23      transfer(newTable);
24      table = newTable;
25      threshold = (int)(newCapacity * loadFactor);
26}

   从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个Entry[]。 
   如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍。如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。

五  重点提示:

   1  遗留集合:JDK1.0或1.1中就有的集合类。


   1) Vector: 向量。可以认为Vector就是旧版的ArrayList。Vector是同步的,即线程安全。首选ArrayList
   2) Stack:栈。具有LIFO的功能。JDK1.6中建议使用Deque,Vetcor的子类。

   3) Hashtable:可以认为是旧版的HashMap。Hashtable是同步的,即线程安全。
                       HashMap是不同步的,线程不安全。
   4) Properties:属性集。它是Hashtable的子类。线程安全的。

      a) Properties可保存在流中或从流中加载。
      b) 属性列表中每个键及其对应值都是一个字符串。不是泛型类。
      c) 不建议使用put和get方法存放键值对。应该改用setProperty(String key, String value);和String getProperty(String key);
   

   2 . 集合工具类Collections:它针对集合提供了一些常用算法。

                   如:排序,混排,查找,求极值等。所有的方法都是静态的。

  1) public static void sort(List<T> list); 元素的自然顺序对指定列表按升序排序。
     
          Collections.sort(List<T> list, Comparator<? super T> c) 方法。
      
           Arrays.sort(T[] a, Comparator<? super T> c) 方法、

  2) public static void shuffle(List<?> list);混排。洗牌算法

  3) public static int binarySearch(List<? extends Comparable<? super T>> list,T key): 使用二分搜索法搜索指定列表,以获得指定对象。在进行此调用之前,必须根据列表元素的自然顺序对列表进行升序排序

  4) public static T max(Collection<? extends T> coll):根据元素的自然顺序,返回给定 collection 的最大元素。collection 中的所有元素都必须实现 Comparable 接口。
 
  5) public static void reverse(List<?> list):反转指定列表中元素的顺序。

  6) 对集合类进行同步处理:
     public static <T> Collection<T> synchronizedCollection(Collection<T> c):返回指定 collection 支持的同步(线程安全的)collection。

3  一些补充--关键点


  1 HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key。


  2. Map的key和Set都有一个共同的特性就是集合的唯一性.TreeMap更是多了一个排序的功能.

  3. hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可
       a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.
       b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象才可以真正定位到键值对应的Entry.
      c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value

 4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的.

      a. Comparator可以在创建TreeMap时指定
      b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.
      c. TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了.


 5、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。 

 6、Set和Collection拥有一模一样的接口。 

 7、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get) 

 8、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。 

 9、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。 
        

     HashMap会利用对象的hashCode来快速找到key。 
    *     hashing 
          哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。 
          我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。 
          发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。 


 10、Map中元素,可以将key序列、value序列单独抽取出来。 
              使用keySet()抽取key序列,将map中的所有keys生成一个Set。 
              使用values()抽取value序列,将map中的所有values生成一个Collection。 
     为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。 

 11  怎么选择合适的集合类-->根据实际的需求
    1) 存放的数据
      需要有序-List
     无序-Set
     “key-value”对-Map
   2) 读的效率和改的效率
      Array* - 读快改慢
      Linked* - 改快读慢
     Hash* - 介于两者之间

12  并发集合类

参考:
          http://android.yaohuiji.com/archives/3411
  
          http://hybxiaodao.iteye.com/blog/1319646
        
          http://blog.csdn.net/qjyong/article/details/4533577(Java集合学习大纲

          http://my.oschina.net/ksfzhaohui/blog/127080 (线程安全容器类

原创粉丝点击