【java基础核心大总结(八)】

集合

集合在我们的日常开发中所使用的次数简直太多了,你已经把它们都用的熟透了,但是作为一名合格的程序员,你不仅要了解它的基本用法,你还要了解它的源码;存在即合理,你还要了解它是如何设计和实现的,你还要了解它的衍生过程。

这篇博客就来详细介绍一下 Collection 这个庞大集合框架的家族体系和成员,让你了解它的设计与实现。

是时候祭出这张神图了

首先来介绍的就是列表爷爷辈儿的接口- Iterator

Iterable 接口

实现此接口允许对象成为 for-each 循环的目标,也就是增强 for 循环,它是 Java 中的一种语法糖

List<Object> list = new ArrayList();
for (Object obj: list){}

除了实现此接口的对象外,数组也可以用 for-each 循环遍历,如下:

Object[] list = new Object[10];
for (Object obj: list){}

其他遍历方式

jdk 1.8之前Iterator只有 iterator 一个方法,就是

Iterator<T> iterator();

实现次接口的方法能够创建一个轻量级的迭代器,用于安全的遍历元素,移除元素,添加元素。这里面涉及到一个 fail-fast 机制。

总之一点就是能创建迭代器进行元素的添加和删除的话,就尽量使用迭代器进行添加和删除。

也可以使用迭代器的方式进行遍历

for(Iterator it = coll.iterator(); it.hasNext(); ){
    System.out.println(it.next());
}

顶层接口

Collection 是一个顶层接口,它主要用来定义集合的约定

List 接口也是一个顶层接口,它继承了 Collection 接口 ,同时也是 ArrayList、LinkedList 等集合元素的父类

Set 接口位于与 List 接口同级的层次上,它同时也继承了 Collection 接口。Set 接口提供了额外的规定。它对add、equals、hashCode 方法提供了额外的标准。

Queue 是和 List、Set 接口并列的 Collection 的三大接口之一。Queue 的设计用来在处理之前保持元素的访问次序。除了 Collection 基础的操作之外,队列提供了额外的插入,读取,检查操作。

SortedSet 接口直接继承于 Set 接口,使用 Comparable 对元素进行自然排序或者使用 Comparator 在创建时对元素提供定制的排序规则。set 的迭代器将按升序元素顺序遍历集合。

Map 是一个支持 key-value 存储的对象,Map 不能包含重复的 key,每个键最多映射一个值。这个接口代替了Dictionary 类,Dictionary 是一个抽象类而不是接口。

ArrayList

ArrayList 是实现了 List 接口的可扩容数组(动态数组),它的内部是基于数组实现的。它的具体定义如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {...}
  • ArrayList 可以实现所有可选择的列表操作,允许所有的元素,包括空值。ArrayList 还提供了内部存储 list 的方法,它能够完全替代 Vector,只有一点例外,ArrayList 不是线程安全的容器。
  • ArrayList 有一个容量的概念,这个数组的容量就是 List 用来存储元素的容量。
  • ArrayList 不是线程安全的容器,如果多个线程中至少有两个线程修改了 ArrayList 的结构的话就会导致线程安全问题,作为替代条件可以使用线程安全的 List,应使用 Collections.synchronizedList
List list = Collections.synchronizedList(new ArrayList(...))
  • ArrayList 具有 fail-fast 快速失败机制,能够对 ArrayList 作出失败检测。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生 fail-fast,即抛出 ConcurrentModificationException 异常。
Vector

Vector 同 ArrayList 一样,都是基于数组实现的,只不过 Vector 是一个线程安全的容器,它对内部的每个方法都简单粗暴的上锁,避免多线程引起的安全性问题,但是通常这种同步方式需要的开销比较大,因此,访问元素的效率要远远低于 ArrayList。

还有一点在于扩容上,ArrayList 扩容后的数组长度会增加 50%,而 Vector 的扩容长度后数组会增加一倍。

LinkedList 类

LinkedList 是一个双向链表,允许存储任何元素(包括 null )。它的主要特性如下:

  • LinkedList 所有的操作都可以表现为双向性的,索引到链表的操作将遍历从头到尾,视哪个距离近为遍历顺序。
  • 注意这个实现也不是线程安全的,如果多个线程并发访问链表,并且至少其中的一个线程修改了链表的结构,那么这个链表必须进行外部加锁。或者使用
List list = Collections.synchronizedList(new LinkedList(...))
Stack

堆栈是我们常说的后入先出(吃了吐)的容器 。它继承了 Vector 类,提供了通常用的 push 和 pop 操作,以及在栈顶的 peek 方法,测试 stack 是否为空的 empty 方法,和一个寻找与栈顶距离的 search 方法。

第一次创建栈,不包含任何元素。一个更完善,可靠性更强的 LIFO 栈操作由 Deque 接口和他的实现提供,应该优先使用这个类

Deque<Integer> stack = new ArrayDeque<Integer>()
HashSet

HashSet 是 Set 接口的实现类,由哈希表支持(实际上 HashSet 是 HashMap 的一个实例)。它不能保证集合的迭代顺序。这个类允许 null 元素。

  • 注意这个实现不是线程安全的。如果多线程并发访问 HashSet,并且至少一个线程修改了set,必须进行外部加锁。或者使用 Collections.synchronizedSet() 方法重写。
  • 这个实现支持 fail-fast 机制。
TreeSet

TreeSet 是一个基于 TreeMap 的 NavigableSet 实现。这些元素使用他们的自然排序或者在创建时提供的Comparator 进行排序,具体取决于使用的构造函数。

  • 此实现为基本操作 add,remove 和 contains 提供了 log(n) 的时间成本。
  • 注意这个实现不是线程安全的。如果多线程并发访问 TreeSet,并且至少一个线程修改了 set,必须进行外部加锁。或者使用
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...))
  • 这个实现持有 fail-fast 机制。
LinkedHashSet 类

LinkedHashSet 继承于 Set,先来看一下 LinkedHashSet 的继承体系:

LinkedHashSet 是 Set 接口的 Hash 表和 LinkedList 的实现。这个实现不同于 HashSet 的是它维护着一个贯穿所有条目的双向链表。此链表定义了元素插入集合的顺序。注意:如果元素重新插入,则插入顺序不会受到影响。

  • LinkedHashSet 有两个影响其构成的参数: 初始容量和加载因子。它们的定义与 HashSet 完全相同。但请注意:对于 LinkedHashSet,选择过高的初始容量值的开销要比 HashSet 小,因为 LinkedHashSet 的迭代次数不受容量影响。
  • 注意 LinkedHashSet 也不是线程安全的,如果多线程同时访问 LinkedHashSet,必须加锁,或者通过使用
Collections.synchronizedSet
  • 该类也支持fail-fast机制
PriorityQueue

PriorityQueue 是 AbstractQueue 的实现类,优先级队列的元素根据自然排序或者通过在构造函数时期提供Comparator 来排序,具体根据构造器判断。PriorityQueue 不允许 null 元素。

  • 队列的头在某种意义上是指定顺序的最后一个元素。队列查找操作 poll,remove,peek 和 element 访问队列头部元素。
  • 优先级队列是无限制的,但具有内部 capacity,用于控制用于在队列中存储元素的数组大小。
  • 该类以及迭代器实现了 Collection、Iterator 接口的所有可选方法。这个迭代器提供了 iterator() 方法不能保证以任何特定顺序遍历优先级队列的元素。如果你需要有序遍历,考虑使用 Arrays.sort(pq.toArray())
  • 注意这个实现不是线程安全的,多线程不应该并发访问 PriorityQueue 实例如果有某个线程修改了队列的话,使用线程安全的类 PriorityBlockingQueue
HashMap

HashMap 是一个利用哈希表原理来存储元素的集合,并且允许空的 key-value 键值对。HashMap 是非线程安全的,也就是说在多线程的环境下,可能会存在问题,而 Hashtable 是线程安全的容器。HashMap 也支持 fail-fast 机制。HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。可以使用 Collections.synchronizedMap(new HashMap(...)) 来构造一个线程安全的 HashMap。

TreeMap 类

一个基于 NavigableMap 实现的红黑树。这个 map 根据 key 自然排序存储,或者通过 Comparator 进行定制排序。

  • TreeMap 为 containsKey,get,put 和remove方法提供了 log(n) 的时间开销。

  • 注意这个实现不是线程安全的。如果多线程并发访问 TreeMap,并且至少一个线程修改了 map,必须进行外部加锁。这通常通过在自然封装集合的某个对象上进行同步来实现,或者使用 SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...))

  • 这个实现持有fail-fast机制。

LinkedHashMap 类

LinkedHashMap 是 Map 接口的哈希表和链表的实现。这个实现与 HashMap 不同之处在于它维护了一个贯穿其所有条目的双向链表。这个链表定义了遍历顺序,通常是插入 map 中的顺序。

  • 它提供一个特殊的 LinkedHashMap(int,float,boolean) 构造器来创建 LinkedHashMap,其遍历顺序是其最后一次访问的顺序。

  • 可以重写 removeEldestEntry(Map.Entry) 方法,以便在将新映射添加到 map 时强制删除过期映射的策略。

  • 这个类提供了所有可选择的 map 操作,并且允许 null 元素。由于维护链表的额外开销,性能可能会低于HashMap,有一条除外:遍历 LinkedHashMap 中的 collection-views 需要与 map.size 成正比,无论其容量如何。HashMap 的迭代看起来开销更大,因为还要求时间与其容量成正比。

  • LinkedHashMap 有两个因素影响了它的构成:初始容量和加载因子。

  • 注意这个实现不是线程安全的。如果多线程并发访问LinkedHashMap,并且至少一个线程修改了map,必须进行外部加锁。这通常通过在自然封装集合的某个对象上进行同步来实现 Map m = Collections.synchronizedMap(new LinkedHashMap(...))

  • 这个实现持有fail-fast机制。

Hashtable 类

Hashtable 类实现了一个哈希表,能够将键映射到值。任何非空对象都可以用作键或值。

  • 此实现类支持 fail-fast 机制
  • 与新的集合实现不同,Hashtable 是线程安全的。如果不需要线程安全的容器,推荐使用 HashMap,如果需要多线程高并发,推荐使用 ConcurrentHashMap
IdentityHashMap 类

IdentityHashMap 是比较小众的 Map 实现了。

  • 这个类不是一个通用的 Map 实现!虽然这个类实现了 Map 接口,但它故意违反了 Map 的约定,该约定要求在比较对象时使用 equals 方法,此类仅适用于需要引用相等语义的极少数情况。
  • 同 HashMap,IdentityHashMap 也是无序的,并且该类不是线程安全的,如果要使之线程安全,可以调用Collections.synchronizedMap(new IdentityHashMap(...))方法来实现。
  • 支持 fail-fast 机制
WeakHashMap 类

WeakHashMap 类基于哈希表的 Map 基础实现,带有弱键。WeakHashMap 中的 entry 当不再使用时还会自动移除。更准确的说,给定key的映射的存在将不会阻止 key 被垃圾收集器丢弃。

  • 基于 map 接口,是一种弱键相连,WeakHashMap 里面的键会自动回收
  • 支持 null 值和 null 键。
  • fast-fail 机制
  • 不允许重复
  • WeakHashMap 经常用作缓存

Collections 类

Collections 不属于 Java 框架继承树上的内容,它属于单独的分支,Collections 是一个包装类,它的作用就是为集合框架提供某些功能实现,此类只包括静态方法操作或者返回 collections。

同步包装

同步包装器将自动同步(线程安全性)添加到任意集合。 六个核心集合接口(Collection,Set,List,Map,SortedSet 和 SortedMap)中的每一个都有一个静态工厂方法。

public static  Collection synchronizedCollection(Collection c);
public static  Set synchronizedSet(Set s);
public static  List synchronizedList(List list);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);
public static  SortedSet synchronizedSortedSet(SortedSet s);
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);

不可修改的包装

不可修改的包装器通过拦截修改集合的操作并抛出 UnsupportedOperationException,主要用在下面两个情景:

  • 构建集合后使其不可变。在这种情况下,最好不要去获取返回 collection 的引用,这样有利于保证不变性
  • 允许某些客户端以只读方式访问你的数据结构。 你保留对返回的 collection 的引用,但分发对包装器的引用。 通过这种方式,客户可以查看但不能修改,同时保持完全访问权限。

这些方法是:

public static  Collection unmodifiableCollection(Collection<? extends T> c);public static  Set unmodifiableSet(Set<? extends T> s);public static  List unmodifiableList(List<? extends T> list);public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m);public static  SortedSet unmodifiableSortedSet(SortedSet<? extends T> s);public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);

线程安全的Collections

Java1.5 并发包 (java.util.concurrent) 提供了线程安全的 collections 允许遍历的时候进行修改,通过设计iterator 为 fail-fast 并抛出 ConcurrentModificationException。一些实现类是CopyOnWriteArrayListConcurrentHashMapCopyOnWriteArraySet

Collections 算法

此类包含用于集合框架算法的方法,例如二进制搜索,排序,重排,反向等。

集合实现类特征图

下图汇总了部分集合框架的主要实现类的特征图,让你能有清晰明了看出每个实现类之间的差异性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值