带着问题来阅读
1、Java有哪些集合
2、不同集合的应用场景分别是哪些
3、哪些实现类是线程安全的
4、为什么Java集合不能存放基本类型
5、集合的fail-fast和fail-safe是什么
Java集合概览Java通过Java Collections Framework(JCF)为开发者提供了一系列集合接口和实现 , 所谓集合 , 就是多个Java对象的聚集 。
学习过数据结构的同学们对各类集合的定义肯定不陌生 , Java通过提供一系列的内置数据结构实现 , 为开发者提高了开发的便利性 , 提升了程序的兼容性 , 降低了编程的复杂性 。

文章插图
图片出自https://www.pdai.tech/md/java/collection/java-collection-all.html
Java集合包含两个顶层接口:Collection和Map , Collection是主要保存对象的集合 , Map是保存键值对的集合 。
下面我们对两类集合的实现做简单介绍 。
CollectionCollection是Jdk1.2版本引入的接口 , 用于描述保存对象的集合 , 它的扩展接口有List、Queue、Set 。
ListList以列表形式顺序存取元素 , 保证元素的插入顺序和存储顺序一致 。
实现线程安全ArrayList数组否LinkedList双向链表否CopyOnWriteArrayList数组是 , 使用CopyOnWrite保证线程安全Vector数组是 , 使用Synchronized保证线程安全Stack数组是 , 继承VectorVector是Jdk1.0就引入的线程安全的列表实现 , 早于Collection接口设计 , 采用直接在方法上添加Synchronized来保证线程安全 , Stack是继承Vector实现的栈结构 , 由于其线程安全的低效性 , 目前在实际环境均不再推荐使用 。
QueueQueue是先进先出的结构 , 从队尾加入元素 , 队头弹出元素 。其子接口Deque为双端队列 , 即两头都可以进出 。
实现线程安全ArrayDeque循环数组否LinkedList链表否PriorityQueue堆否BlockingQueueBlockingQueue为阻塞队列的扩展接口是由于BlockingQueue略为复杂 , 更涉及到一些进阶应用场景 , 留待后续讲解 。
SetSet是不重复元素集合 , 元素中不包含相同元素 , 且通常情况不保持元素插入顺序
实现线程安全HashSet哈希表否 , 基于HashMap实现TreeSet红黑树否 , 基于TreeMap实现LinkedHashSet哈希表+链表否 , 基于LinkedHashMap实现CopyOnWriteArraySet数组是 , CopyOnWrite保证ConcurrentSkipListSet跳表是 , 基于ConcurrentSkipListMap实现MapMap用于存储<Key, Value>键值对 。
实现线程安全HashMap哈希表+红黑树否TreeMap红黑树否LinkedHashMap哈希表+链表+红黑树否WeakHashMap哈希表否ConcurrentHashMap哈希表+红黑树是 , 基于节点CAS和Synchronized实现ConcurrentSkipListMap跳表是为什么Java集合不能存放基本类型Java在1.2版本引入JCF框架 , Java范型是在1.5版本引入 , 因此在泛型引入之前集合默认以Object作为存储类型 。
以List为例List list = new ArrayList();list.add(123); // 自动boxinglist.add("123");int num = (int) list.get(0);String str = (String) list.get(1);显而易见该方式存在缺陷 , 集合内可以放入任何以Object为基类的元素 , 而元素的获取方无法确定元素的具体类型 , 容易出现类型转换错误 。在1.5版本引入范型以后 , 对集合接口进行规范 , 添加了范型参数 , Java的泛型机制本质上还是将具体类型擦除为Object , 因此泛型集合在初始化时 , 无法将参数指定为非Object派生的基本类型 。什么是fail-fast和fail-safe
List<String> list = new ArrayList();list.add("123");list.add("456");//(1) throw ConcurrentModificationExceptionfor (String s : list) { list.remove(s);}//(2) 正常移除Iterator<String> it = list.iterator();while (it.hasNext()) { it.next(); it.remove();}//(3) throw ConcurrentModificationExceptionnew Thread(() -> { for (String s : list) {System.out.println(s);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException ignore){} }}).start();new Thread(() -> {list.add("789");}).start();上面这段代码 , (1) (3) 会抛出ConcurrentModificationException , (2)可以正常移除所有元素 。首先了解一下ConcurrentModificationException , 当对一个对象做出的并发修改不被允许时 , 将抛出这个异常 。This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
那么(1)是单线程执行 , (3)并发添加元素也并未对已有元素做修改 , 为什么也会触发该异常呢 。
ArrayList var1 = new ArrayList();var1.add("123");var1.add("456");Iterator var2 = var1.iterator();while(var2.hasNext()) {String var3 = (String)var2.next();var1.remove(var3);}对代码(1)的class文件反编译查看 , 发现foreach实际上是通过Iterator做的迭代 , 迭代过程中删除是直接调用list.remove 。我们再进入到list.iterator方法探个究竟 。/** Returns an iterator over the elements in this list in proper sequence. *The returned iterator is fail-fast. */public Iterator<E> iterator() {return new Itr();}private class Itr implements Iterator<E> {....int expectedModCount = modCount;....final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}iterator方法会创建一个Itr对象 , 在其创时会复制modCount到expectedModCount , 每进行迭代时都会判断两个值是否相同 , 如果不同则抛出ConcurrentModificationException 。再来看modCount是一个继承自AbstractList的成员变量 , 用于记录list被修改的次数 , 每当调用add/remove时 , modCount都会加1 。
// The number of times this list has been structurally modified.protected transient int modCount = 0;那么问题就很明显了 , 每当对list进行修改modCount都会改变 , 而foreach的iterator记录的是迭代对象创建时刻的modCount值 , 接下来的迭代过程中 , 由于调用了list的修改方法 , 改变了其中modCount的值 , 导致modCount != expectedModCount , 于是就抛出了异常 。(3)代码是相同的问题 , 不再进行赘述 。* <p><a name="fail-fast">* The iterators returned by this class's {@link #iterator() iterator} and* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>* if the list is structurally modified at any time after the iterator is* created, in any way except through the iterator's own* {@link ListIterator#remove() remove} or* {@link ListIterator#add(Object) add} methods, the iterator will throw a* {@link ConcurrentModificationException}.Thus, in the face of* concurrent modification, the iterator fails quickly and cleanly, rather* than risking arbitrary, non-deterministic behavior at an undetermined* time in the future.在所有Java集合类中 , 直接位于java.util下除Vector、Stack、HashTable外 , 所有的集合都是fail-fast的 , 而在java.util.concurrent下的集合都是fail-safe的 , 即可以并发的遍历和修改集合 , 具体实现由各自的线程安全机制保证 。为什么需要fail-fast
【Java有哪些集合类 JAVA集合类概览】fail-fast意为快速失败 , 在非线程安全的集合应用场景中 , 并发对集合做的添加/删除 , 可能导致另一个正在遍历集合的线程出现未知的错误如数组越界 。因此非线程安全的集合实现引入fail-fast以此来快速中断线程 , 避免引发未知的连锁问题 。
参考
- Java集合框架详解
- 快速失败(fail-fast)与安全失败(fail-safe)
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
