Java 容器相关知识全面总结
Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题。因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList三剑客, 平时接触使用的容器也只有ArrayList和HashMap。导致对于整个Java容器体系的掌握和使用还停留在很浅的层面。省不足而思改进,那么跟着我来总结一下Java容器的相关知识吧。
结构
- java容器类的继承结构
- 具体介绍
- 迭代器
- Collection
- List
- Set
- Queue
- Map
- 一些建议
- 进阶·并发容器
- CopyOnWriteArrayList与Copy-On-Write策略
- ConcurrentLinkedQueue
- ConcurrentHashMap与锁分段技术
- 阻塞队列
java容器类的继承结构
Java容器类库定义了两个不同概念的容器,Collection和Map
- Collection 一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素。Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序。

(文中Jdk源码版本无特殊说明均为jdk1.8.0_101)
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(java.util.Collection<?> c);
boolean addAll(java.util.Collection<? extends E> c);
boolean removeAll(java.util.Collection<?> c);
... //省略了其他方法
}
可以看到,java定义了Collection接口和内部集合的基本操作方法,Collection默认可以进行对集合末端添加元素,删除指定元素等操作。List、Set、Queue接口都继承自Collection并定义了各自不同的方法。
- Map 一组成对的”键值对”对象,允许我们使用键来查找值。

public interface Map<K,V> {
int size();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(java.util.Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<java.util.Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
...
}
boolean equals(Object o);
int hashCode();
}
Map内部接口Entry<K,V>对应着Map的键值对。
具体介绍
迭代器
先介绍一下迭代器。迭代器本身也是一种设计模式,设计的初衷在于:容器的实现由很多种,而我们想对容器进行遍历操作的话,首先不应该关心容器实现的细节,其次遍历操作应该是轻量级的。迭代器统一了对容器的访问方式,同时创建它的代价很小。值得注意的是,Iterator只能单向移动。
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
通过容器的iterator()方法拿到容器的迭代器
迭代器的next()获取下一个元素
hasNext()判断是否还有元素
remove()删除指定元素
ListIterator
ListIterator是Iterator的扩展之内,用于各种List类访问,支持双向移动。
Collection
List
List 承诺可以将元素维护在特定的序列中.List接口在Collection的基础上添加了大量的方法,使得可以再List中间插入和移除元素。
public interface List<E> extends Collection<E> {
...
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
java.util.List<E> subList(int fromIndex, int toIndex);
...
}

有两种类型的List,ArrayList和LinkedList
| List类型 | 优点 | 缺点 | 底层实现 |
|---|---|---|---|
| ArrayList | 随机访问元素较快 | 中间元素的插入和删除较慢 | 数组 |
| LinkedList | 中间元素的插入和删除,顺序访问的优化 | 随机访问元素较慢 | 双向链表 |
Set
Set不保存重复的元素,通常用于快速查找元素。值得一提的是,Set具有与Collection完全一样的接口,没有任何额外的功能。 存入的元素必须定义equals()方法
| Set类型 | 使用场景 | 底层实现 |
|---|---|---|
| HashSet | 快速查找,元素必须定义hashCode() | 链表 |
| TreeSet | 保持次序,元素必须实现Comparable接口 | 红-黑树结构 |
| LinkedHashSet | 维护次序的HashSet, 元素必须定义hashCode() | 链表 |

Queue
除了并发应用,Queue仅有的两个实现是LinkedList和PriorityQueue, 其中LinkedList同时实现了List, Deque接口。它们的差异在于排序行为而不是性能。
public interface Queue<E> extends Collection<E> {
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
}

Map
| Map类型 | 使用场景 | 底层实现 |
|---|---|---|
| HashMap | 快速查询 | 散列表 |
| LinkedHashMap | 迭代遍历具有顺序(插入顺序 or 最近最少使用) | 链表 |
| TreeMap | 具有排序,唯一可以返回子树的Map(subMap()) | 红-黑树结构 |
| WeakHashMap | 弱键映射,映射之外无引用的键,可以被垃圾回收 | 散列表 |
| ConcurrentHashMap | 线程安全的Map | 链表 |
| IdentityHashMap | 使用==代替equals()对键进行排序,专位解决特殊问题 | 链表 |

我们可以手工调整HashMap来调整性能,涉及到如容量、初始容量、尺寸、负载因子等概念。感兴趣的话可以看一些相关资料。
一些建议
- 不要使用过时的容器 如Vector Enumeration Hashtable Stack(没错,这就是java最初的糟糕设计,实际中使用栈的话推荐LinkedList)
进阶·并发容器
这里不会讨论的太细致的实现,仅仅简单介绍一下基础知识,感兴趣的可以阅读《Java 并发编程的艺术》这本书。
CopyOnWriteArrayList与Copy-On-Write策略
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
ConcurrentLinkedQueue
在并发编程中,有时候需要使用线程安全的队列或列表。通常实现线程安全有两种方式,一种是使用阻塞算法,一种是使用非阻塞算法。非阻塞算法实现基础为循环CAS(Compare and Swipe 比较和交换)。
ConcurrentLinkedQueue技术上的实现与CopyOnWriteArrayList与Copy类似,但是容器只有部分内容而不是整个容器可以被复制和修改。ConcurrentLinkedQueue有head节点和tail节点组成,每个节点由节点元素(item)和指向下一个结点(next)的引用组成。节点之间通过next关联起来,形成一张链表结构的队列。
ConcurrentHashMap与锁分段技术
ConcurrentHashMap是线程安全且高效的HashMap。多线程环境下,使用非线程安全的HashMap会导致死循环,而如文章中建议的那样,HashTable这种过时容器效率低下(使用synchronized来保证线程安全)。ConcurrentHashMap使用锁分段技术,大大提高了并发使用的效率。
锁分段技术: 假设容器有多把锁,每一把锁用于锁容器其中一部分数据,当多线程访问容器不同数据段数据时,线程间就不存在锁竞争,从而提高并发访问效率。
阻塞队列
JDK7 提供了7个阻塞队列,实现原理都是基于生产-消费模式的等待通知机制。
| 阻塞队列类型 | 特点 |
|---|---|
| ArrayBlockingQueue | 由数组结构组成的有界阻塞队列 |
| LinkedBlockingQueue | 由链表结构组成的有界阻塞队列 |
| PriorityBlockingQueue | 支持优先级排序的无界阻塞队列 |
| DelayQueue | 使用优先级队列实现的无界阻塞队列 |
| SynchronousQueue | 不储存元素的阻塞队列 |
| LinkedTransferQueue | 由链表结构组成的无界阻塞队列 |
| LinkedBlockingQueue | 由链表结构组成的双向阻塞队列 |
感谢阅读~
Java 容器相关知识全面总结的更多相关文章
- 【转】java NIO 相关知识
原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...
- 工作随笔—Java容器基础知识分享(持有对象)
1. 概述 通常,程序总是运行时才知道的根据某些条件去创建新对象.在此之前,不会知道所需对象的数量,甚至不知道确切的类型,为解决这个普遍的编程问题:需要在任意时刻和任意位置创建任意数量的对象,所以,就 ...
- Java容器相关知识点整理
结合一些文章阅读源码后整理的Java容器常见知识点.对于一些代码细节,本文不展开来讲,有兴趣可以自行阅读参考文献. 1. 思维导图 各个容器的知识点比较分散,没有在思维导图上体现,因此看上去右半部分很 ...
- 4)Java容器类相关知识
1>Array 和 Arrays: Arrays:用来操作array的工具类,其中包含一组static函数: equals():比较两个array 是否相等. array拥有相同元 ...
- Java 容器一些知识
一.Collection 1.static 方法: Collections.sort(List<T>):实现List排序功能 Collections.fill(List<T> ...
- Java GC相关知识
Java堆的分类 分为两类:YoungGen和OldGen.其中,YoungGen分为三部分:eden,from survivor和to survivor,比例默认是:8:1:1 PermGen不属于 ...
- Java继承相关知识总结
Java继承的理解 一.概念: 一个新类从已有的类那里获得其已有的属性和方法,这种现象叫类的继承 这个新类称为子类,或派生类,已有的那个类叫做父类,或基类 继承的好处:代码得到极大的重用.形成一种类的 ...
- Java异常相关知识总结
异常: 概述:java程序运行过程中出现的错误 常见的异常: StackOverflowError ArrayIndexOutOfBoundsException NullPointerExceptio ...
- Java枚举相关知识
JAVA枚举 很多编程语言都提供了枚举的概念,但是java直到1.5之后才提出了枚举的概念,出现比这个语言本身晚10年. 主要作用是用于定义有限个数对象的一种结构(多例设计),枚举就属于多例设计并且其 ...
随机推荐
- libsvm+detector_(libsvm参数说明)
细分析了cvhop.cpp中的compute函数,可以直接调用它来获得样本HOG,然后训练得到检测算子 1.制作样本2.对每一张图片调用hog.compute(img, descriptors,Siz ...
- gcc编译代码报错及编译方式
一.error: 'for' loop initial declarations are only allowed in C99 mode 前段时间写了一个小C程序,放在linux下用gcc编译出错, ...
- 在Windows 上的 Python
在 Windows 上, 安装 Python 有两种选择. ActiveState 制作了一个 Windows 上的 Python 安装程序称为 ActivePython, 它包含了一个完整的 Pyt ...
- 22.allegro中PCB打印设置[原创]
1. -- 2. 3. 4. ----
- WEBBROWSER中模拟鼠标点击(SendMessage/PostMessage)
好久没有写文章,发一篇顶顶博客访问量.别人建议转一些比较好的代码也贴过来,但是我打算这里主要发自己原创的代码,所以么..流量该多少就多少吧... 回到主题,在webbrowser中点击某链接网上几乎都 ...
- inline-block在ie6中的经典bug
众所周知,给元素设置 inline-block ,可以让ie下的元素出发layout:1. 但是,当给元素设置 inline-block 后,在另外一个class 样式(非设置inline-block ...
- 1033. Labyrinth(dfs)
1033 简单dfs 有一点小小的坑 就是图可能不连通 所以要从左上和右下都搜一下 加起来 从讨论里看到的 讨论里看到一句好无奈的回复 “可不可以用中文呀...” #include <iostr ...
- topcoder srm 628 div2 250 500
做了一道题,对了,但是还是掉分了. 第二道题也做了,但是没有交上,不知道对错. 后来交上以后发现少判断了一个条件,改过之后就对了. 第一道题爆搜的,有点麻烦了,其实几行代码就行. 250贴代码: #i ...
- iOS开发:在Xcode中用Pods管理第三方库
之前写了一篇 iOS开发:在Swift中调用oc库 ,今天记录一下如何用Pods的方式来管理第三方库,包括Swift/Object-C的库. 在这之前请先查阅Guides.CocoaPods如何使用的 ...
- IIS修改队列长度(IIS6+IIS7)
Internet Information Services (IIS) 限制了在任何给定时间可在队列中等待的应用程序池请求的最大数量.如果达到此限制,则所有新请求都将被拒绝,而且用户将收到错误消息“5 ...