Java集合类

1、概念

  • 数组与集合的区别

    • 数组长度不可变化而且无法保存具有映射关系的数据;集合类用于保存数量不确定的数据,以及保存具有映射关系的数据
    • 数组元素既可以是基本类型的值,也可以是对象;集合只能保存对象

  • Java集合类存放于 java.util 包中,是一个用来存放对象的容器

    • 集合只能存放对象

    • 集合存放的是多个对象的引用,对象本身还是放在堆内存中

    • 集合可以存放不同类型,不限数量的数据类型

  Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系,(注意:Map不是Collection的子接口)。

  其中List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合;Map代表的是存储key-value对的集合,可根据元素的key来访问value。

2、架构图

3、集合类的详解

  3.1、Collection接口

    Collection接口是处理对象集合的根接口,其中定义了很多对元素进行操作的方法,AbstractCollection是提供Collection部分实现的抽象类。  

  3.2、Set集合类

    继承自Collection。不能存在相同的对象,无序的,就是数学意义上的集合。具体实现类有HashSet,LinkedHashSet,TreeSet等。

    3.2.1、HashSet

      HashSet 底层是使用了哈希表来支持的,特点:存取速度快。往Hashset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值,然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。

    3.2.2、LinkedHashSet

      LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同,因此 LinkedHashSet 的实现上非常简单,只提供了四个构造方法。并通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可。

    3.2.3、TreeSet

      treeSet 底层是以红-黑树的数据结构实现的,默认对元素进行自然排序(String)。 如果在比较的时候两个对象返回值为0,那么元素重复。

      自然排序:TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素的大小关系,然后将元素按照升序排列,这就是自然排序。如果试图将一个对象添加到TreeSet集合中,则该对象必须实现Comparable接口,否则会抛出异常。当一个对象调用方法与另一个对象比较时,例如obj1.compareTo(obj2),如果该方法返回0,则两个对象相等;如果返回一个正数,则obj1大于obj2;如果返回一个负数,则obj1小于obj2。对于TreeSet集合而言,它判断两个对象是否相等的标准是:两个对象通过compareTo(Object obj)方法比较是否返回0,如果返回0则相等。

      定制排序:想要实现定制排序,需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由Comparator对象负责集合元素的排序逻辑。

  综上:自然排序实现的是Comparable接口,定制排序实现的是Comparator接口。(具体代码实现会在后续章节中讲解)

    3.2.4、EnumSet类

      EnumSet是一个专为枚举类设计的集合类,不允许添加null值。EnumSet的集合元素也是有序的,它以枚举值在Enum类内的定义顺序来决定集合元素的顺序。

    3.2.5、Set实现类的性能分析

      HashSet的性能比TreeSet的性能好(特别是添加,查询元素时),因为TreeSet需要额外的红黑树算法维护元素的次序,如果需要一个保持排序的Set时才用TreeSet,否则应该使用HashSet。

      LinkedHashSet是HashSet的子类,由于需要链表维护元素的顺序,所以插入和删除操作比HashSet要慢,但遍历比HashSet快。

      EnumSet是所有Set实现类中性能最好的,但它只能 保存同一个枚举类的枚举值作为集合元素。

      以上几个Set实现类都是线程不安全的,如果多线程访问,必须手动保证集合的同步性

  3.3、List集合类

    List接口扩展自Collection,它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。AbstractList类提供了List接口的部分实现,AbstractSequentialList扩展自AbstractList,主要是提供对链表的支持。具体实现类有ArrayList,LinkedList,Vector等(已经被废弃,很少使用)。

    3.3.1、ArrayList

      最常用的List接口实现类,底层使用可变长度的动态数组实现。ArrayList有一个初始容量(capacity = 10),当元素数量大于初始容量时进行扩容,新的数组长度 = 旧数组长度 + 旧数组长度 / 2;

      因为每个元素都有固定的位置索引,所以根据索引查询元素的速度非常快。如果在中间插入元素时,由于后面的元素全部要后移一位,所以性能会比较差;

      由于没有做并发访问控制,所以它是一个非线程安全的集合。允许重复元素或null元素。

    3.3.2、LinkedList

      List接口的双向链接的实现类,允许NULL元素。它表现上是一个有序的集合,但内存中其实是无序保存,所以它插入的速度会很快,但是查询一个元素的速度较ArrayList速度慢很多。是一个非线程安全的集合。

  3.4、Map集合类

    以键值对的形式存放对象。key-value。一般是key为String类型,value为Object的类型。具体实现类有HashMap,LinkedHashMap,TreeMap等。

    3.4.1、HashMap与Hashtable

      HashMap与Hashtable是Map接口的两个典型实现,它们之间的关系完全类似于ArrayList与Vertor。HashTable是一个古老的Map实现类,它提供的方法比较繁琐,目前基本不用了。

  HashMap与Hashtable主要存在以下两个典型区别:

    HashMap是线程不安全,HashTable是线程安全的。

    HashMap可以使用null值最为key或value;Hashtable不允许使用null值作为key和value,如果把null放进HashTable中,将会发生空指针异常。

  为了成功的在HashMap和Hashtable中存储和获取对象,用作key的对象必须实现hashCode()方法和equals()方法。

  HashMap工作原理如下:

    HashMap基于hashing原理,通过put()和get()方法存储和获取对象。当我们将键值对传递给put()方法时,它调用建对象的hashCode()方法来计算hashCode值,然后找到bucket位置来储存值对象。当获取对象时,通过建对象的equals()方法找到正确的键值对,然后返回对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会存储在链表的下一个节点中。

    3.4.2、LinkedHashMap

      LinkedHashMap使用双向链表来维护key-value对的次序(其实只需要考虑key的次序即可),该链表负责维护Map的迭代顺序,与插入顺序一致,因此性能比HashMap低,但在迭代访问Map里的全部元素时有较好的性能。

    3.4.3、TreeMap

      TreeMap是SortedMap的实现类,是一个红黑树的数据结构,每个key-value对作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap也有两种排序方式:

  自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则会抛出ClassCastException。

  定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。

     3.4.4、Map实现类的性能分析

      HashMap通常比Hashtable(古老的线程安全的集合)要快

      TreeMap通常比HashMap、Hashtable要慢,因为TreeMap底层采用红黑树来管理key-value。

      LinkedHashMap比HashMap慢一点,因为它需要维护链表来爆出key-value的插入顺序。

  3.5、迭代器Iterator

    3.5.1、概念

    迭代器(Iterator)是一种设计模式、提供了一种方法,来对集合、容器进行遍历的方式,不需要关注底层数据结构和数据类型,来达到底层和上层遍历解耦的目的

    3.5.2、迭代器方法

    boolean hasNext() :判断集合是否还有元素; true表示还存在元素 ,false表示不存在元素

    E next():返回当前数据

    void remove():删除元素

  3.6、ListIterator接口

    ListIterator接口继承Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口的基础上增加了以下几个方法:

    boolean hasPrevious():判断集合里是否存在上一个元素。如果有,该方法返回 true。
    Object previous():返回集合里上一个元素。
    void add(Object o):在指定位置插入一个元素。

posted @ 2020-09-07 12:21  小破孩123  阅读(208)  评论(0编辑  收藏  举报