Java基础容器
前言
本文转载自学习笔记
基本接口
java 提供了一些基础容器类,可以用特定的方式组织、存储和操作对象数据。这些集合框架分为两大分支:Collection 接口和 Map 接口。
所有容器都定义在 java.util 文件夹内,使用时需要进行导入。
Collection 接口
【集合】用特定的方式组织、存储和操作对象数据。有三个常用子接口 List 接口、Queue 接口、Set 接口。
Collection 接口以及所有子接口和子方法 都定义在 java.util 文件夹内,使用时需进行导入。
1 | // 修改 |
List 接口
【列表】元素有序,可以按索引操作。
1 | // 修改 |
Queue 接口
【队列】元素有序,在队列尾插入/在队列首移除。常用 Deque 子接口。
1 | //修改 |
Deque 接口
【双端队列】元素可以在两端进出。
1 | deque.offerFirst(e); // 队列首添加元素 |
Set 接口
【集】数据不可重复。
1 | // 修改 |
HashSet 类无序,因此不支持 get 方法:获取对象必须要通过 Iterator 来遍历。
Collections 类
Collections 类是针对集合类的一个帮助类,他提供一系列静态方法实现各种集合操作。
- 排序操作(主要针对List接口)
1 | Collections.swap(list, 1, 2); // 元素交换顺序 |
- 查找和替换
1 |
|
- 上锁(主要针对List接口)
调用 Collections 类中的 synchronizedList 方法,可以将 List 接口转换成线程安全的容器使用。
List 接口中的方法都会被添加 synchronized 锁(效率不高)。但是 iterator 方法没有加锁,如果要遍历还需要在外层加锁。
1 | List list = Collections.synchronizedList(new ArrayList()); |
Map 接口
1 | map.put("key_1",1); // 添加键值对,已有 key 则覆盖 value |
线性存储
ArrayList 类
【数组序列】实现了 List 接口,内部使用 Object 数组存储:
- 可以高效地按索引进行元素修改和查询。
- 添加元素时动态扩容:当容量满后,ArrayList 类会新建一个 1.5 倍容量的新数组,然后将当前数组数据全部复制过去。
ArrayList 构造方法
1 | List<Integer> list = new ArrayList<>(); // 默认初始容量为 10 |
LinkedList 类
【链表序列】实现了 List 和 Deque 接口。内部使用双向链表存储:
- 可以高效地进行元素插入和删除。
- 容量无限。
LinkedList 构造方法
1 | List<String> list = new LinkedList<>(); // 创建空对象 |
ArrayDeque 类
【数组双端队列】实现了 Deque 接口。内部使用 Object 数组存储(不允许存储 null 值):
- 可以高效进行元素查找和尾部插入取出,是用作队列、双端队列、栈甚至递归树的绝佳选择。
- 添加元素时动态扩容:当容量满后,ArrayDeque 类会新建一个 1.5 倍容量的新数组,然后将当前数组数据全部复制过去。
ArrayDeque 构造方法
1 | ArrayDeque<String> queue = new ArrayDeque<>(); // 创建空对象 |
PriorityQueue 类
【无界优先级队列】实现了 Queue 接口。内部使用 Object 数组存储(不允许存储 null 值):
- PriorityQueue 类内会自动对元素进行排序,是作为堆的绝佳选择。但实际在数组中并不是有序存储,而只保证队首元素是最小值:每次弹出队首元素后会自动查找剩余队列中的最小元素放到队首。
- 添加元素时动态扩容:当容量满后,PriorityQueue 类会新建一个 1.5 倍容量的新数组,然后将当前数组数据全部复制过去。
PriorityQueue 构造方法
开发者在构造队列时可通过重写 compare 方法自定义排序规则。如果存储未重写 compareTo 方法的自定义对象,则必须重写 compare 方法。
1 | // 默认排序方法 |
哈希存储
HashMap 类
【哈希表】 实现 Map 接口。底层使用散列存储:构造一个 Entry 数组,根据 key 的 hash 值将 Entry 存入指定位置。
- key 值无序且不可重复,且允许 null 作为 key 值存在。
- 发生哈希冲突时,HashMap 采用链表保存多个元素。当链表长度大于 8 时,链表自动转化为红黑树。
- 达到负载因数后,HashMap 将调用 resize 方法动态扩容:新建一个 2 倍容量的新数组复制当前数组的数据。
HashMap 构造方法
1 | Map<String,Integer> map = new HashMap<>(); // 默认初始容量 16 负载因数 0.75 |
LinkedHashMap 类
【链式哈希表】继承 HashMap 类。
- 底层使用散列存储:构造一个 Entry 数组,根据 key 的 hash 值将 Entry 存入指定位置。
- Entry 额外添加了引用 before & after ,使哈希表内的所有 Entry 构成一个双向链表维护 Entry 的顺序。
LinkedHashMap 构造方法
在默认情况下 Entry 按照插入顺序排序,可指定创建时的初始容量和负载因数。
1 | Map<String,Integer> map = new LinkedHashMap<>(); // 默认初始容量 16 负载因数 0.75 |
Entry 也可以按照访问顺序排序:对 Entry 进行操作时会先删除再插入,将 Entry 移动到双向链表的表尾。
1 | Map<String,Integer> map = new LinkedHashMap<>(32,0.5f, true); // 基于访问顺序排序 |
LinkedHashMap 类提供了 removeEldestEntry 方法,在使用 put 操作插入 Entry 时将自动调用此方法决定是否移除双向链表表头的 Entry:默认返回 false ,可通过重写此方法以实现 LRU 算法。
1 | // Entry 超过容量后自动删除最久未使用的 Entry |
TreeMap 类
【树表】 实现了 Map 接口。底层使用红黑树存储:Entry 按照 key 值大小插入红黑树,并动态调整红黑树高度。
TreeMap 类方法
TreeMap 类提供了以下专属方法使用。
1 | map.firstKey(); // 返回最小 key |
Set 子类
HashSet 类:【散列集】基于 HashMap 类实现。
LinkedHashSet 类:【链式散列集】基于 LinkedHashMap 类实现。
TreeSet 类:【树集】基于 TreeMap 类实现。
元素遍历
遍历容器
Iterable 接口
是集合框架的顶级接口,被所有容器类都实现。
- 提供 iterator 方法,用来创建一个实现了 Iterator 接口的 iterator 对象:按容器类规定的顺序实现遍历集合。
- JDK 1.8 引入 foreach 方法遍历集合。效率更高,但不能对元素进行删除操作,否则会抛出异常。
Iterator 接口
提供了 hasNext、next、remove 三个方法,可以按容器类规定的顺序实现遍历集合。
遍历顺序
List / Queue 接口
- 全部方法:按数组或链表顺序输出。
Map / Set 接口
- HashSet/HashMap 类:在返回数据时没有特别的顺序。
- LinkedHashSet/LinkedHashMap 类:默认按插入顺序返回数据,也可以按访问顺序返回。
- TreeSet/TreeMap 类:在返回数据时按 key 值从小到大排列,即按照树的中序遍历返回。
遍历方法
Collection 接口
1 | List<String> list = new ArrayList<>(); |
Map 接口
1 | Map<String,String> map=new HashMap<String,String>(); |
遍历失败
在迭代元素的时候不能通过集合的方法修改或删除元素,但可以通过迭代器的 remove 方法删除元素。
- java.util 包下面的所有的集合类都是快速失败的。直接对原容器进行修改,会抛出 ConcurrentModificationException 异常。
- java.util.concurrent 包下面的所有的集合类都是安全失败的。遍历时先对底层集合做拷贝再遍历,因此不会抛出异常。








