java Collection集合知识点整理(疯狂java讲义读书笔记)

来源:互联网 发布:淘宝内部优惠券微信群 编辑:程序博客网 时间:2024/06/05 19:07

JDK文档:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh


这里写图片描述


Collection和Iterator接口:

Collection接口:

Collection接口是List, Set, Queue接口的父接口,该接口里定义的方法既可以用于操作Set集合,也可以用于操作List和Queue集合。Collection接口里定义了如何操作集合元素的方法。如下图:

这里写图片描述

知识点: 当使用System.out的println方法来输出集合对象时,将输出[ele1,ele2,….]的形式,这显然是因为所有的Collection实现类都重写了toString()方法,该方法可以一次性的输出集合中的所有元素。

Iterator接口:

Iterator接口也是集合框架的成员,但它与Collection系列, 和Map系列的集合不一样; Colletion系列集合,Map系列的集合主要用来盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器对象。

Iterator接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一的编程接口。Iterator接口定义了如下3个方法:

这里写图片描述

注意:当使用iterator迭代访问集合元素的时候,Collection集合里的元素不能被改变,只有通过Iterator的
的remove方法删除上一次next返回的集合元素才可以;否则将会引发 java.util.ConcurrentModificationException异常。

例1:迭代的时候直接修改Collection的元素,引发异常!

      public static void main(String[] args){        Collection collection=new ArrayList();        collection.add("A");        collection.add("B");        collection.add("C");        java.util.Iterator iterator = collection.iterator();        while (iterator.hasNext()){            String str = (String) iterator.next();            if(str.equals("A")){                //throw java.util.ConcurrentModificationException                collection.remove(str);            }            System.out.println(str);        }        System.out.println(collection);    }

例2:迭代的时候使用Iterator进行删除。正常!

    public static void main(String[] args){        Collection collection=new ArrayList();        collection.add("A");        collection.add("B");        collection.add("C");        java.util.Iterator iterator = collection.iterator();        while (iterator.hasNext()){            String str = (String) iterator.next();            if(str.equals("A")){                iterator.remove();            }            System.out.println(str);        }        System.out.println(collection);    }

Set集合

Set集合里的多个对象没有明显的顺序。Set集合与Collection基本完全一样,它没有提供任何额外的方法。实际上Set就是Collection,只是行为略有不同(Set不允许包含重复元素)。
Set集合不允许包含重复的元素,如果试图把两个相同的元素加入到一个Set集合中,则添加操作失败,add返回false. 且新元素不会被加入。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象;反之,只要这两个对象用equals方法比较返回false,Set集合就会接受这两个对象(甚至这两个对象是一个对象,Set也可以把它们当成两个对象处理)。

HashSet类:

HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个工具类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet具有以下特点:
1.不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。

3.集合元素的值可以为null。**

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode()值,然后根据该HashCode值决定该对象在HashSet中的位置。如果有两个元素通过equals()方法比较返回为true,但是它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。
简单的说:HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。

LinkedHashSet:

HashSet的子类,LinkedHashSet也是根据元素的HashCode值来决定元素的存储位置,但它同时使用链表来维护元素的插入顺序,这样看起来元素是以插入顺序进行保存的。也就是说当遍历LinkedHashSet集合的元素时,LinkedHashSet将会按照元素的添加顺序来访问集合的元素。
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时将有很好的性能,因为它是以链表来维护内部顺序的。

TreeSet:

TreeSet是一个SortedSet接口实现类,TreeSet可以确保集合元素处于排序状态。
特点:
1.TreeSet并不是根据元素的插入顺序进行排序的,而是根据元素的实际大小来进行排序的。

2.与HashSet集合采用hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构来存储数据。

3.TreeSet支持两种排序,1: 自然排序 2:定制排序

自然排序 :
TreeSet会调用集合元素的compareTo(Object object)的方法来比较元素之间的大小关系,然后将集合元素按升序排序。这种方式就是自然排序。所以添加到TreeSet中的接口要实现Comparable接口的compareTo(Object object)的方法。

注意点1:因为TreeSet集合中要使用compareTo方法进行排序,而大多数compareTo方法的实现都要进行类型转换。所以放入TreeSet集合中的元素应该是同一个元素的实例,否则就会导致类型转换异常。

注意点2:当把一个对象加入到TreeSet集合中,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树结构找到它的存储位置。如果两个对象通过compareTo(Object obj)方法返回相等,新对象将无法添加到TreeSet集合中。

定制排序:
TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,例如以降序排序,则可以通过Compatator接口的帮助。该接口里包含一个int compare(T o1,To2)方法,该方法用于比较o1和o2的大小,如果该方法返回正整数,则表明o1大于o2。如果该方法返回0,则表明o1等于o2。如果该方法返回负整数,则表明o1小于o2。
如果要实现定制排序,则需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator 对象负责集合元素的排列顺序。

例:下面代码,根据M类的age属性实现倒序排序:

  public static void main(String[] args){           TreeSet<M> customCompareTreeSet=new TreeSet<>(new Comparator<M>() {            @Override            public int compare(M o1, M o2) {                return o1.age>o2.age? 1:o1.age<o2.age?-1:0;            }        });        customCompareTreeSet.add(new M(2));        customCompareTreeSet.add(new M(1));        customCompareTreeSet.add(new M(3));        System.out.println(customCompareTreeSet);    } static class M {        int age;        public M(int age){            this.age=age;        }        @Override        public String toString() {            return age+"";        }    }

注意:HashSet和TreeSet放入可变对象的时候,处理这些对象会很复杂,而且容易出错。所以推荐HashSet和TreeSet只放入不可变对象。

各Set实现类的性能分析:

  1. HashSet性能总是比TreeSet好(特别是最常用的添加,查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
  2. LinkedHashSet对于普通的插入和删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的开销造成的;不过因为有了链表LinkedHashSet遍历会更快。
  3. 需要注意的时候LinkedHashSet,TreeSet,HashSet都不是线程安全的,所以如果有多个线程访问集合中的元素, 并且有超过一个线程修改了该Set集合,则必须手动保证该集合的同步性。

List集合

List集合代表一个元素有序,可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为1….
List除了继承collection接口的一些方法外,因为List是有序集合,因此List集合里增加了一些根据索引来操作集合元素的方法。
List还提供了一个ListIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口基础上增加了如下方法:可以在倒序的迭代集合,也可以在迭代集合的时候添加元素。

ListIterator增加的方法:

这里写图片描述

ArrayList和Vector实现类:

ArrayList和Vector作为List类的两个典型实现,完全支持List接口的全部功能。
特点:
1. ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类分配了一个动态的可以重新分配的Object[]数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加。

2.通常情况下,程序员无需关系ArrayList或Vector的initialCapacity。但如果向ArrayList或Vector集合中添加大量的元素时,可使用ensureCapacity(int minCapacity)方法一次性地增加initialCapacity。这可以减少重分配的次数,从而提高性能。


Queue:

Queue 用于模拟队列这种数据结构,队列通常是指“先进先出”(FIFO)的容器。队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回头部的元素。通常,队列不允许随机访问队列中的元素。

队列定义的常用方法:

这里写图片描述

PriorityQueue队列:

PriorityQueue是一个标准的队列实现类。之所以说它是比较标准的队列实现,而不是绝对标准的队列实现,是因为PriorityQueue保存的队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek()方法或者poll()方法取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。从这个意义上来看, PriorityQueue已经违反了队列的最基本规则:先进先出(FIFO)。
特点:
PriorityQueue队列不允许插入null元素,它还需要对队列元素信息排序。
PriorityQueue有两种排序方式.
1.自然排序:
采用自然顺序的 PriorityQueue集合中的元素必须实现Compareable接口,而且应用是同一个类的多个实例,否则可能导致ClassCastExcepation异常。

2.定制排序:
创建PriorityQueue队列时,传入一个Compareable对象,该对象负责对队列中所有的元素进行排序。采用定制排序时不要求元素实现Compareable接口。

Deque接口和ArrayDeque实现类:

Deque接口是Queue接口的子接口,它代表一个双端队列,Deque接口里定义了一些双端队列的方法,这些方法允许从两端来操作队列的元素。
Deque接口不仅可以当成双端队列使用,而且也可以被当成栈来使用,因为该类还包含pop(出栈),push(入栈)两个方法。
ArrayDeque是Deque接口的典型实现,是一个基于数组实现的双端队列,创建Deque的时候同样可以指定一个numElements参数,该参数用于指定Object[]数组的长度,如果不指定numElements参数,Deque底层数组的长度为16.

LinkedList实现类

LinkedList类是List接口的实现类,可以根据索引来随机访问集合中的元素。 LinkedList还实现了Deque接口,因此它可以被当成双端队列来使用,自然也可以被当成栈来使用。
LinkedList与ArrayList,ArrayDeque的实现机制完全不同,ArrayList,ArrayDeque内部以数组的形式保存集合中的元素,因此随机访问时有较好的性能,而LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素的时候性能较差,但在插入,删除元素时性能非常出色(只需要改变指针的位置即可)。Vector也是以数组的形式来存储集合元素时,但因为它实现了线程同步功能,所以各方面性能都有所下降。

各线性表的性能分析

集合名称 实现机制 随机访问排名 迭代操作排名 插入操作排名 删除操作排名 数组 连续内存区保存元素 1 不支持 不支持 不支持 ArrayList/ArrayDeque 以数组保存元素 2 2 2 2 vector 以数组保存元素 3 3 3 3 LinkedList 以链表保存元素 4 1 1 1