0、前言
首先提出几个问题,Java为什么要提供集合框架?没有集合框架的话我们还能编程吗?集合框架需要提供哪些基本的功能?
我们知道,程序本质上是通过算法对数据的操作,这样看,当有了基本类型和引用类型的数据类型以后,我们便可以创建变量/对象并通过给变量/对象赋值实现逻辑和数据的流转。然而实际上,我们要面临的问题是,我们往往不是操作某个单一变量/对象,而是要批量操作数据,这样就要求编程语言能够一次性对一批相同类型的变量/对象做处理,需要能够承载这一批对象的容器,集合的概念应运而生。
通常,集合要具有(但不限于)以下功能:
- 能够添加、删除元素
- 能够计算本身大小
- 能够增大、减小本身容量
- 能够迭代遍历每个元素
1、Java集合框架
Java集合框架根据源头接口实现可以分为Collection和Map,Collection是一组有序或者无序的元素集合,Map是一组<key, value>
键值对集合(EntrySet)。
Collection和Map的层次结构(hierarchy)如图所示
1.1、Java Collection
1.1.1、Collection的行为
Collection接口继承了Iterable接口,Iterable具有以下行为:
-
iterator():返回一个迭代器Iterator;
-
forEach():遍历元素,并执行传入的参数方法。
而迭代器Iterator作为一个接口,具有以下行为:
- forEachRemaining:这是Java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素
- hasNext:判断容器内是否还有可供访问的元素
- next:返回迭代器刚越过的元素的引用,返回值是 Object,需要强制转换成自己需要的类型
- remove:删除迭代器刚越过的元素
1.1.2、Collection的层次结构
Collection有三个子接口,分别是List,Set和Queue。
1.1.2.1、List
1、List的行为
2、List的特点
- 元素有序
- 可重复
- 允许面向索引位置操作元素
3、List的子接口或实现
- ArrayList
- Vendor
- LinkedList
ArrayList | Vendor | LinkedList | Stack | |
---|---|---|---|---|
线程安全 | 否 | 是 | 否 | 是 |
底层实现 | 动态数组 | 数组 | 双向链表 | |
特点 | 查询快,增删慢 | 最慢 | 增删快,查询慢 | 继承自Vector,后进先出 |
1.1.2.2、Set
1、Set的行为
2、Set的特点
- 无序的
- 不允许重复元素(和hashcode与equals方法直接相关)
- 最多有一个null元素
3、Set的子接口或实现
- HashSet
- TreeSet
- LinkedHashSet
- EnumSet
HashSet | TreeSet | LinkedHashSet | |
---|---|---|---|
线程安全 | 否 | 否 | 否 |
底层实现 | 哈希表(HashMap) | 红黑树(TreeMap) | 哈希表和链表(LinkedHashMap) |
特点 | 无序,最多一个null元素 | 有序,元素可为null | 有序,元素可为null |
1.1.2.3、Queue
1、Queue的行为
2、Queue的特点
- 一个队列就是一个先入先出(FIFO)的数据结构
- 继承了Collection接口
- 子接口Deque(LinkedList实现)
3、Queue的子接口或实现
- 非阻塞队列
- PriorityQueue:维护了一个有序列表。加入到 Queue 中的元素根据它们的天然排序(通过其 java.util.Comparable 实现)或者根据传递给构造函数的 java.util.Comparator 实现来定位。
- ConcurrentLinkedQueue:基于链接节点的、线程安全的队列。并发访问不需要同步。因为它在队列的尾部添加元素并从头部删除它们,所以只要不需要知道队列的大小,ConcurrentLinkedQueue 对公共集合的共享访问就可以工作得很好。收集关于队列大小的信息会很慢,需要遍历队列。
- 阻塞队列-BlockingQueue 接口
- ArrayBlockingQueue:一个由数组支持的有界队列。
- LinkedBlockingQueue:一个由链接节点支持的可选有界队列。
- PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列。
- DelayQueue:一个由优先级堆支持的、基于时间的调度队列。
- SynchronousQueue:一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。
1.2、Java Map
Map是由一系列键值对组成的集合,提供了key到Value的映射。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,value值可以相同。
1.2.1、Map的行为
1.2.2、Map的层次结构
Map有5个具体的实现(HashMap/Hashtable/TreeMap/IdentityHashMap/WeakHashMap)和一个接口(SortedMap)
1.2.2.1、HashMap
- 非线程安全的
- 底层实现:数组+链表,当链表长度大于8时转数组+红黑树
- 长度始终是2的幂次方
- 扩容:2n
- 允许一个key=null,多个value=null
LinkedHashMap继承自HashMap,它保留元素顺序,因此性能稍逊于HashMap
1.2.2.2、TreeMap
- 非线程安全
- 底层实现:基于红黑树,每一个key-value节点作为红黑树的一个节点
- 有序
- 自然排序:TreeMap中所有的key必须实现Comparable接口,并且所有的key都应该是同一个类的对象,否则会报ClassCastException异常。
- 定制排序:定义TreeMap时,创建一个comparator对象,该对象对所有的treeMap中所有的key值进行排序,采用定制排序的时候不需要TreeMap中所有的key必须实现Comparable接口。
- 判断两个元素相等:两个key通过compareTo()方法返回0,则认为这两个key相等。如果使用自定义的类来作为TreeMap中的key值,且想让TreeMap能够良好的工作,则必须重写自定义类中的equals()方法,TreeMap中判断相等的标准是:两个key通过equals()方法返回为true,并且通过compareTo()方法比较应该返回为0。
1.2.2.3、Hashtable
- 线程安全,内部的方法都经过synchronized修饰(但推荐使用CurrentHashMap)
- 底层实现:
- key=null时,抛出NullPointerException
- 扩容:2n+1
1.2.2.4、IdentityHashMap
略
1.2.2.5、WeakHashMap
略
2、附
2.1、集合和数组的区别
2.1.1、长度限制之别
- 数组长度是固定不变的
- 集合的大小是可动态变化的
2.1.2、存储类型之别
- 一个数组存储的元素可以是基本类型,也可以是引用类型,且只能存储同一种类型的元素
- 一个集合存储的元素只能是引用类型,但集合可以存储不同类型的元素(但集合一般存储同一种类型,可以用泛型加以控制)
2.1.3、访问元素方式
- 数组是根据索引来获取元素的
- 集合通常会提供一个迭代器来方便访问元素
2.2、集合工具类-Collections
2.3、数组工具类-Arrays
2.4、集合常见面试问题
1、说一说List,Set,Map的区别?
2、ArrayList和LinkedList区别?
3、ArrayList和Vector的区别?
4、ArrayList的扩容?
5、HashMap的扩容?
6、Hashtable的扩容?
7、HashMap和Hashtable的区别?
8、HashMap和HashSet的区别?
9、HashMap的底层实现?
10、HashSet如何检查重复?
11、CurrentHashMap的底层实现?
12、CurrentHashMap和Hashtable的区别?
13、HashMap多线程操作时为什么容易造成死循环?