本篇主要来介绍一下Java中的集合框架;
简化图如下:
对于以上框架需要说明:所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,Collection派生出了List和Set两大分支,Map派生出了TreeMap和HashMap两个分支;
一、总体分析
分析:
1、Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection包含了List和Set两大分支。
2、Map是一个映射接口,即key-value键值对。Map中的每一个元素包含“一个key”和“key对应的value”。AbstractMap是个抽象类,它实现了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是继承于AbstractMap。Hashtable虽然继承于Dictionary,但它实现了Map接口。
3、Iterator。它是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。ListIterator是专门为遍历List而存在的。
4、Enumeration,它是JDK 1.0引入的抽象类。作用和Iterator一样,也是遍历集合;但是Enumeration的功能要比Iterator少。在上面的框图中,Enumeration只能在Hashtable, Vector, Stack中使用。
5、最后,看Arrays和Collections。它们是操作数组、集合的两个工具类。
以上对集合的整体框架进行了分析,接下来我们来挨个分析集合框架的每一个类;
二、Collection接口
Collection接口是处理对象集合的根接口,其中定义了很多对元素进行操作的方法。如下图所示:
其中有些比较常用的方法:
add()添加一个元素到集合中;
addAll()添加所有元素到集合中;
contains()检查集合中是否包含指定元素;
另外:Collection中有一个iterator()函数,它的作用是返回一个Iterator接口。
我们可以通过Iterator迭代器来遍历集合。(注意)ListIterator是List接口所特有的,在List接口中,通过ListIterator()返回一个ListIterator对象。
Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口,这个要牢记。
1、List接口
特点:有序、允许重复元素
List集合代表一个有序集合,继承于Collection接口,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
(1)ArrayList(非同步)
ArrayList是一个动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。
(2)LinkedList(非同步)
LinkedList是一个双向链表。链表无容量限制,但双向链表本身使用了更多的空间,也需要额外的链表指针操作,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。
(3)Vector(同步)
与ArrayList相似,Vector是线程安全的动态数组,它的操作与ArrayList几乎一样。
(4)Stack(同步)
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供了额外的方法使得Vector得以被当作堆栈使用。
图示总结
2、Set接口
特点:无序、不允许重复元素
Set不允许包含相同的元素,Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。
Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。
(1)HashSet(非同步、无序)
HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序(这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),而且HashSet允许使用null 元素。 HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。
注意
HashSet中是允许存入null值的,但是在HashSet中仅仅能够存入一个null值。
HashSet中存储的元素的是无序的,由于HashSet底层是基于Hash算法实现的,使用了hashcode(hashcode的介绍下面会详细说明),所以HashSet中相应的元素的位置是固定的。
(2)LinkedHashSet(非同步、有序)
LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的。LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
(3)TreeSet(非同步、有序、可定制排序)
TreeSet是一个有序集合,其底层是基于TreeMap实现的。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造TreeSet时,若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
注意
TreeSet集合不是通过hashcode和equals函数来比较元素的,它是通过compare或者comparaeTo函数来判断元素是否相等;
hashcode说明
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值;有以下特点:
1、HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象equals相等,那么这两个对象的HashCode一定也相同;
3、如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写;
4、如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置;
三、Map接口
Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。
(1)HashMap(非同步)
以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。
特点
Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。遍历时,取得数据的顺序是完全随机的。HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;
(2)HashTable (同步)
Hashtable 是一个散列表,实现了Map,它的key、value都不可以为null。
(3)LinkedHashMap(非同步)
LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。 LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null键和null值。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
特点
LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。
(4)TreeMap(非同步、有序、可定制排序)
TreeMap 是一个有序的key-value集合,非同步,基于红黑树(Red-Black tree)实现,每一个key-value节点作为红黑树的一个节点。TreeMap存储时会进行排序的,会根据key来对key-value键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。TreeMap不允许key的值为null。
图示总结