Java 集合框架

整体框架

Java 集合大致可以分为两大体系,一个是 Collection,另一个是 Map

java.util.Collectionjava.util.Map下的接口和继承关系简单结构图:

8

其中,Java 集合框架中主要封装的是典型的数据结构和算法,如动态数组、双向链表、队列、栈、Set、Map 等

将集合框架挖掘处理,可以分为以下几个部分

有序列表「List」

List集合的特点:存取有序,可以存储重复的元素,可以使用下标操作元素

List主要实现的类:ArrayListLinkedListVectorStack

ArrayList

ArrayList是一个动态数组结构,支持随机存取,尾部插入删除方便,内部插入删除效率低 (因为要移动数组元素);如果内部数组容量不足则自动扩容,因此当数组很大时,效率较低

LinkedList

LinkedList是一个双向链表结构,在任意位置插入删除都很方便,但是不支持随机取值,每次都只能从一端开始遍历,直到找到查询的对象,然后返回

不过,它不像ArrayList那样需要进行内存拷贝,因此相对来说效率较高,但是因为存在额外的前驱和后继节点指针,因此占用的内存比ArrayList多一些

Vector

Vector也是一个动态数组结构,是一个元老级别的类,早在 jdk1.1 就引入该类,之后在 jdk1.2 里引进ArrayListArrayList大部分的方法和Vector比较相似,但两者是不同的,Vector是允许同步访问的,Vector中的操作是线程安全的,但是效率低,而ArrayList所有的操作都是异步的,执行效率高,但不安全!

关于Vector,现在用的很少了,因为里面的getsetadd等方法都加了synchronized,所以,执行效率会比较低,如果需要在多线程中使用,可以采用下面语句创建ArrayList对象

也可以考虑使用复制容器java.util.concurrent.CopyOnWriteArrayList进行操作,例如:

Stack

StackVector的一个子类,本质也是一个动态数组结构,不同的是,它的数据结构是先进后出,取名叫栈!

关于Stack,现在用的也很少,因为有个ArrayDeque双端队列,可以替代Stack所有的功能,并且执行效率比它高!

集「Set」

Set集合的特点:元素不重复存取无序无下标

Set主要实现类:HashSetLinkedHashSetTreeSet

HashSet

HashSet底层是基于HashMapkey实现的,元素不可重复,特性同HashMap

LinkedHashSet

LinkedHashSet底层也是基于LinkedHashMapkey实现的,一样元素不可重复,特性同LinkedHashMap

TreeSet

同样的,TreeSet也是基于TreeMapkey实现的,同样元素不可重复,特性同TreeMap

Set集合的实现,基本都是基于Map中的键做文章,使用Map中键不能重复、无序的特性;所以,我们只需要重点关注Map的实现即可!

队列「Queue」

Queue是一个队列集合,队列通常是指「先进先出 (FIFO)」的容器。新元素插入offer到队列的尾部,访问元素poll操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素

Queue主要实现类:ArrayDequeLinkedListPriorityQueue

ArrayDeque

ArrayQueue是一个基于数组实现的双端队列,可以想象,在队列中存在两个指针,一个指向头部,一个指向尾部,因此它具有「FIFO 队列」及「栈」的方法特性

LinkedList

LinkedListList接口的实现类,也是Deque的实现类,底层是一种双向链表的数据结构

LinkedList同时实现了stackQueuePriorityQueue的所有功能

在上面也有所介绍,LinkedList可以根据索引来获取元素,增加或删除元素的效率较高,如果查找的话需要遍历整合集合,效率较低

PriorityQueue

PriorityQueue也是一个队列的实现类,此实现类中存储的元素排列并不是按照元素添加的顺序进行排列,而是内部会按元素的大小顺序进行排列,是一种能够自动排序的队列

映射表「Map」

Map是一个双列集合,其中保存的是键值对,键要求保持唯一性,值可以重复

Map主要实现类:HashMapLinkedHashMapTreeMapIdentityHashMapWeakHashMapHashtableProperties

HashMap

关于HashMap,相信大家都不陌生,继承自AbstractMapkey不可重复,因为使用的是哈希表存储元素,所以输入的数据与输出的数据,顺序基本不一致

另外,HashMap最多只允许一条记录的keynull

LinkedHashMap

HashMap的子类,内部使用链表数据结构来记录插入的顺序,使得输入的记录顺序和输出的记录顺序是相同的

LinkedHashMapHashMap最大的不同处在于,LinkedHashMap输入的记录和输出的记录顺序是相同的!

TreeMap

能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历时,得到的记录是排过序的

如需使用排序的映射,建议使用TreeMap,TreeMap实际使用的比较少!

IdentityHashMap

继承自AbstractMap,与HashMap有些不同,在获取元素的时候,通过==代替equals ()来进行判断,比较的是内存地址

WeakHashMap

WeakHashMap继承自AbstractMap,被称为缓存Map,向WeakHashMap中添加元素,再次通过键调用方法获取元素方法时,不一定获取到元素值,因为WeakHashMap中的Entry可能随时被 GC 回收

Hashtable

Hashtable是一个元老级的类,键值不能为空,与HashMap不同的是,方法都加了synchronized同步锁,是线程安全的,但是效率上,没有HashMap快!

同时,HashMap 是 HashTable 的轻量级实现,他们都完成了Map 接口,区别在于 HashMap 允许K和V为空,而HashTable不允许K和V为空,由于非线程安全,效率上可能高于 Hashtable。

如果需要在多线程环境下使用HashMap,可以使用如下的同步器来实现或者使用并发工具包中的ConcurrentHashMap

Properties

Properties继承自HashtableProperties新增了load()store()方法,可以直接导入或者将映射写入文件,另外,Properties的键和值都是String类型

比较器

ComparableComparator接口都是用来比较大小的,一般在TreeSetTreeMap接口中使用的比较多,主要用于解决排序问题

Comparable

对实现该接口的每个类的对象进行排序

若一个类实现了Comparable接口,实现Comparable接口的类的对象的List列表 (或数组) 可以通过Collections.sort (或Arrays.sort) 进行排序

此外,实现Comparable接口的类的对象 可以用作「有序映射」(如TreeMap) 中的键或「有序集合」(如TreeSet) 中的元素,而不需要指定比较器

例子:

测试:

输出:

Comparator

对实现该接口的每个类的对象进行排序

如果我们的这个类Person无法修改或者没有继承Comparable接口,我们又要对其进行排序,Comparator就可以派上用场了

将类Person实现的Comparable接口去掉

测试:

上述也可使用 lambda 表示式

常用工具类

Collections 类

java.util.Collections工具类为集合框架提供了很多有用的方法,这些方法都是静态的,在编程中可以直接调用

整个Collections工具类源码差不多有 4000 行,这里只针对一些典型的方法进行阐述

addAll

向指定的集合c中加入特定的一些元素elements

binarySearch

利用二分法在指定的集合中查找元素

sort

shuffle

混排,随机打乱原来的顺序,它打乱在一个List中可能有的任何排列的踪迹

reverse

集合排列反转

synchronized 系列

确保所封装的集合线程安全 (强同步)

Arrays 类

java.util.Arrays工具类也为集合框架提供了很多有用的方法,这些方法都是静态的,在编程中可以直接调用

整个Arrays工具类源码有 3000 多行,这里只针对一些典型的方法进行阐述

asList

将一个数组转变成一个List,准确来说是ArrayList

注意:这个List是定长的,企图添加或者删除数据都会报错java.lang.UnsupportedOperationException

sort

对数组进行排序,适合「byte,char,double,float,int,long,short」等基本类型,还有 Object 类型

binarySearch

通过二分查找法对已排序的数组进行查找

如果数组没有经过Arrays.sort排序,那么检索结果未知

适合「byte,char,double,float,int,long,short」等基本类型,还有 Object 类型和泛型

copyOf

数组拷贝,底层采用System.arrayCopy (native 方法) 实现

适合「byte,char,double,float,int,long,short」等基本类型,还有泛型数组

copyOfRange

数组拷贝,指定一定的范围,底层采用System.arrayCopy (native 方法) 实现

适合「byte,char,double,float,int,long,short」等基本类型,还有泛型数组

equals && deepEquals

equals:判断两个数组的每一个对应的元素是否相等 (对于两个数组的元素aa2a == null ? a2 == null : a.equals(a2))

deepEquals:主要针对一个数组中的元素还是数组的情况 (多维数组比较)

toString && deepToString

toString:将数组转换成字符串,中间用逗号隔开

deepToString:当数组中又包含数组,就不能单纯的利用Arrays.toString()了,使用此方法将数组转换成字符串

迭代器

JCF 的迭代器 (Iterator) 为我们提供了遍历容器中元素的方法。只有容器本身清楚容器里元素的组织方式,因此迭代器只能通过容器本身得到。每个容器都会通过内部类的形式实现自己的迭代器

JDK 1.5 引入了增强的for循环,简化了迭代容器时的写法