Java集合类解析

Java的集合类主要有两个接口派生而出:Collection和Map。


Map保存的每项数据都是key-value对,也就是由key和value两个值组成。Map里的key是不可以重复的,key用于标识集合里的每项数据,如需要查阅Map中的数据,总是根据Map的key来获取。


三种集合示意图如下


如果访问List集合中的元素,可以直接根据元素索引来访问;如果访问Map集合中的元素,可以根据每项元素的key来访问其value;如果访问Set集合中的元素,则只能根据元素本身来访问。

使用Iterator遍历集合元素

Iterator主要用来遍历集合中的元素,Iterator对象也被称为迭代器。Iterator本身并不提供盛装对象的能力。

当用Iterator对象进行迭代时,Iterator只是把集合元素的值传递给了迭代变量,所有修改迭代变量的值对集合元素并没有什么影响。

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

一、Set集合

Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。

1.1 HashSet类

HashSet按Hash算法来存储集合中的元素。

HashSet具有以下特点:

不能保证元素的排列顺序。

HashSet不是同步的,如果多个线程同时访问一个HashSet,则必须通过同步代码来保证其同步。

集合元素值可以是null。

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

注:当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法。规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode值也应该相同

重写hashCode()方法的一般步骤。

1. 把对象内每个有意义的实例变量计算出一个int类型的hashCode值。

2. 用第1步计算出来的多个hashCode值组合计算一个hashCode值返回。

return f1.hashCode() +(int)f2;

为了避免直接相加产生偶然相等,可以通过为各个实例变量的hashCode值乘以任意一个数值后再相加。

return f1.hashCode() *19  +(int)f2 * 31;

1.2 LinkedHashSet类

HashSet的子类,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。

1.3 TreeSet类

SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态

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

TreeSet支持两种排序方法:自然排序和定制排序。在默认情况下,TreeSet采用自然排序。

1. 自然排序

TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素进行升序排列。

Java提供了一个Comparable接口,该接口定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象可以比较大小。当一个对象调用该方法与另一个对象进行比较时,例如obj1.compareTo(obj2),如果该方法返回0,则这两个对象相等;如果返回一个正整数,则obj1大于obj2;如果返回一个负整数,则obj1小于obj2

如果试图把一个对象添加到TreeSet集合中时,则该对象的类必须实现Comparable接口,否则程序将会抛出异常。

如果希望TreeSet能正常运行,TreeSet只能添加同一种类型的对象

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

TreeSet集合判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0

注:当需要把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应保证该方法与compareTo(Object obj)有一致的结果。其规则是:如果两个对象通过equals()方法比较返回true时,这两个对象通过compareTo(Object obj)方法比较返回0

2. 定制排序

TreeSet的自然排序是根据集合元素的大小,TreeSet将他们以升序排列。如果需要实现定制排序,则可以通过Comparator(函数式)接口的帮助。该接口里包含一个int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小如果该方法返回正整数,则o1大于o2;如果该方法返回0,则o1等于o2;如果该方法返回负整数,则o1小于o2

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

定义一个类,实现Comparator接口,覆盖compare方法,将比较器对象作为参数传递给TreeSet集合中的构造函数。

1.4 EnumSet类

EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet是显式或隐式地指定。

1.5 各Set实现类的性能分析

HashSet的性能总是比TreeSet好(特别是常用的添加、查询元素等操作),因为TreeSet需要额外的二叉树算法来维护集合元素的次序。只有当需要一个保持排序的Set集合时,才应该使用TreeSet,否则都应该使用HashSet集合。

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

Set的三个实现类HashSet、TreeSet、EnumSet都是线程不安全的。如果有多个线程同时访问同一个Set集合时,并且有超过一个线程修改了Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来包装该Set集合,此操作好在创建时进行,以防止对Set集合的以外非同步访问。

SortedSet s = Collections.synchronizedSortedSet(new HashSet(...));

二、List集合

List集合代表一个元素有序、可重复的集合。集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。例如第一次添加的元素索引为0,第二次添加的元素索引为1......

List判断两个对象相等只要通过equals()方法比较返回true即可。

ListIterator接口

该方法返回一个ListIterator对象,继承了Iterator接口,增加了向前迭代的功能(Iterator只能向后迭代),而且ListIterator还可通过add()方法向list集合中添加元素(Iterator只能删除元素)。

2.1 ArrayList和Vector实现类

ArrayList和Vector类都是基于数组实现的List类,封装了一个动态的、允许再分配的Object[]数组。ArrayList或Vector对象使用initialCapacity参数来设置数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加。

如果向ArrayList或Vector集合中添加大量元素时,可使用ensureCapacity(int minCapacity)方法一次性地增加initialCapacity。

2.2 固定长度的List

asList把一个数组或指定个数的对象转成一个List集合,它不是ArrayList实现类的实例,它是Arrays的内部类ArrayList的实例

Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不能增加、删除该集合里的元素。

三、Queue集合

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

3.1 PriorityQueue实现类

PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。PriorityQueue不允许插入null元素,它还需要对队列元素进行排序。

3.2 Deque和ArrayDeque类

Deque是一个双端队列,允许从两端来操作队列元素。

Deque接口提供了一个典型的实现类:ArrayDeque,它是一个基于数组实现的双端队列,创建Deque时同样可指定一个numElements参数,该参数用于指定Object[]数组的长度;如果不指定numElements参数,Deque底层数组的长度为16。

3.3 LinkedList实现类

LinkedList类是List接口的实现类,可以根据索引来随机访问集合中的元素。LinkedList还是实现了Deque接口,可以被当成双端队列来使用。

3.4 各种线性表的性能分析

List是一个线性表接口,ArrayList、LinkedList是线性表的两种典型实现,基于数组的线性表和基于链表的线性表。数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能比较好;而内部以链表作为底层实现的集合在执行插入、删除操作时有较好的性能。但总体来说,ArrayList的性能比LinkedList的性能要好。

如果需要遍历List集合元素,对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能最好;对于LinkedList集合,则应该采用迭代器来遍历集合元素。

如果需要经常执行插入、删除操作来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合。使用ArrayList、Vector集合可能需要经常重新分配内部数组的大小,效果可能较差。

如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合

四、Map集合

Map用于保存具有映射关系的数据,Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。Map包含了keySet()方法,用于返回Map里所有key组成的Set集合

Map提供了一个Entry内部类来封装key-value对,而计算Entry存储时则只考虑Entry封装的key。Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合

添加key-value对时,Map允许多个value重复,但如果添加key-value对时Map中已有重复的key,那么新添加的value会覆盖该key原来对应的value,该方法将返回原有的value

HashMap重写了toString()方法,实际上所有的Map实现类都重写了toString()方法,调用Map对象的toString()方法总是返回如下格式的字符串:{key1=value1,key2=value2......}

4.1 HashMap和Hashtable实现类

1. 区别

Hashtable是一个线程安全的Map实现,HashMap是线程不安全的实现。

Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NullPointerException异常;但HashMap可以使用null作为key和value。

由于HashMap里的key不能重复,所以HashMap里最多只有一个key-value对的key为null。

HashMap、Hashtable判断两个key相等的标准是:两个key通过equals()方法比较返回true,两个key的hashCode值也相等

HashMap、Hashtable判断两个value相等的标准是:两个对象通过equals()方法比较返回true即可

4.2 LinkedHashMap实现类

使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致

4.3 使用Properties读写属性文件

Properties是Hashtable的子类,Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的”属性名=属性值“加载到Map对象中。Properties里的key、value都是字符串类型。

Properties可以把key-value对以XML文件的形式保存起来,也可以从XML文件中加载key-value对。

4.4 SortedMap接口和TreeMap实现类

TreeMap底层数据结构是红黑树,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。

TreeMap中判断两个key相等的标准是:两个key通过compareTo()方法返回0。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值