- 通用实现是最常用的实现,专为日常使用而设计。它们在标题为通用实现的表中进行了总结。
- 专用实现旨在用于特殊情况,并显示非标准性能特征,使用限制或行为。
- 并发实现旨在支持高并发性,通常以牺牲单线程性能为代价。这些实现是
java.util.concurrent
包的一部分。 - 包装器实现与其他类型的实现(通常是通用实现)结合使用,以提供增加或限制的功能。
- 便利实现是通常通过静态工厂方法提供的小型实现,为特殊集合(例如,单例集)的通用实现提供方便,有效的替代方案。
- 抽象实现是骨架实现,有助于构建自定义实现 - 稍后将在“ 自定义集合实现”部分中进行介绍。
通用实现接口哈希表实现可调整大小的数组实现树实现链接列表实现哈希表+链表实现Set
HashSet
TreeSet
LinkedHashSet
List
ArrayList
LinkedList
Queue
Deque
ArrayDeque
LinkedList
Map
HashMap
TreeMap
LinkedHashMap
你可以从表中看到,Java集合框架提供了几种通用的实现
Set
,List
以及Map
接口。在每种情况下,一个实现 -HashSet
,ArrayList
和HashMap
- 显然是大多数应用程序使用的,所有其他条件相同。请注意,表SortedSet
和SortedMap
接口在表中没有行。每个接口都有一个实现 (TreeSet
和TreeMap
),并列在行Set
和Map
行中。有两个通用Queue
实现 -LinkedList
也是一个List
实现,并且PriorityQueue
从表中省略。这两个实现提供了非常不同的语义:LinkedList
提供FIFO语义,同时PriorityQueue
根据其值对其元素进行排序。每个通用实现都提供其接口中包含的所有可选操作。所有允许
null
元素,键和值。无同步(线程安全)。它们都具有快速失败的迭代器,它可以在迭代期间检测非法并发修改,并且可以快速,干净地失败,而不是在未来的未确定时间冒任意,不确定的行为。所有人都Serializable
支持公共clone
方法。这些实现是不同步的这一事实代表了对过去的打破:遗留集合
Vector
并且Hashtable
是同步的。采用本方法是因为当同步没有任何好处时经常使用集合。这些用途包括单线程使用,只读使用,以及用作进行自身同步的大型数据对象的一部分。一般来说,良好的API设计实践不会让用户为他们不使用的功能付费。此外,在某些情况下,不必要的同步可能导致死锁通常,您应该考虑接口,而不是实现。这就是本节中没有编程示例的原因。在大多数情况下,实施的选择仅影响性能。接口部分中提到的首选样式
Collection
是在创建a时选择实现,并立即将新集合分配给相应接口类型的变量(或将集合传递给期望接口参数的方法类型)。通过这种方式,程序不会依赖于给定实现中的任何添加方法,使程序员可以随时通过性能问题或行为细节保证更改实现。-
并发映射实现
所述
java.util.concurrent
包中包含的ConcurrentMap
接口,它延伸Map
与原子putIfAbsent
,remove
和replace
方法,以及ConcurrentHashMap
该接口的实现。ConcurrentHashMap
是一个由哈希表备份的高度并发,高性能的实现。执行检索时,此实现永远不会阻塞,并允许客户端选择更新的并发级别。它旨在作为以下内容的替代品Hashtable
:除了实现之外ConcurrentMap
,它还支持所有特有的遗留方法Hashtable
。同样,如果您不需要遗留操作,请小心使用该ConcurrentMap
界面对其进行操作。 -
以下部分简要讨论了实现。使用诸如常数时间,对数,线性,n log(n)和二次方的单词来描述实现的性能,以指代执行操作的时间复杂度的渐近上限。所有这些都是相当满口的,如果你不知道它意味着什么并不重要。如果您有兴趣了解更多信息,请参阅任何优秀的算法教科书.
有三种通用 Set
的实现- HashSet
, TreeSet
和 LinkedHashSet
。使用这三种中的哪一种通常是直截了当的。HashSet
比TreeSet
大多数操作的常数时间与对数时间快得多,但没有提供订购保证。如果需要在SortedSet
界面中使用操作,或者需要按值进行迭代,请使用TreeSet
; 否则,使用HashSet
。这是一个公平的赌注,你最终会在HashSet
大部分时间里使用。
LinkedHashSet
在某种意义上介于HashSet
和之间TreeSet
。它实现为一个哈希表,其中包含一个链接列表,它提供了插入顺序的迭代(最近最少插入到最近)并且运行速度几乎一样快HashSet
。该LinkedHashSet
实现使其客户免于提供的未指定的,通常混乱的排序,HashSet
而不会产生与之相关的增加的成本TreeSet
。
值得注意的一件事HashSet
是,迭代在条目数和桶数(容量)之和中是线性的。因此,选择太高的初始容量会浪费空间和时间。另一方面,选择一个太低的初始容量会在每次强制增加容量时复制数据结构,从而浪费时间。如果未指定初始容量,则默认值为16.过去,选择素数作为初始容量有一些优势。这不再是真的。在内部,容量总是四舍五入到2的幂。使用int
构造函数指定初始容量。以下代码行分配HashSet
其初始容量为64 的代码。
设置<String> s = new HashSet <String>(64);
该HashSet
班有叫另外一个调整参数客座率。如果您非常关心自己的空间消耗,请HashSet
阅读HashSet
文档以获取更多信息。否则,只接受默认值; 它几乎总是正确的做法。
如果您接受默认加载因子但想要指定初始容量,请选择一个大约是您希望该组增长的大小的两倍的数字。如果您的猜测偏远,您可能会浪费一些空间,时间或两者,但这不太可能是一个大问题。
LinkedHashSet
具有相同的调整参数HashSet
,但迭代时间不受容量影响。TreeSet
没有调整参数。
专用设备实现
有两个特殊用途的Set
实现 - EnumSet
和 CopyOnWriteArraySet
。
EnumSet
是Set
枚举类型的高性能实现。枚举集的所有成员必须具有相同的枚举类型。在内部,它由位向量表示,通常是单个向量long
。枚举在枚举类型的范围上设置支持迭代。例如,给定星期几的枚举声明,您可以迭代工作日。本EnumSet
类提供了一个静态的工厂,可以很容易。
for(Day d:EnumSet.range(Day.MONDAY,Day.FRIDAY))
的System.out.println(d);
枚举集还为传统的位标志提供了丰富的,类型安全的替代品。
EnumSet.of(Style.BOLD,Style.ITALIC)
CopyOnWriteArraySet
是一个Set
由写时复制数组备份的实现。所有可变操作,如add
,set
,和remove
,通过使所述阵列的一个新的复制来实现; 不需要锁定。甚至迭代也可以安全地与元素插入和删除同时进行。不像大多数Set
实施中,add
,remove
,和contains
方法需要的时间与集合的大小。此实现仅适用于很少修改但经常迭代的集合。它非常适合维护必须防止重复的事件处理程序列表。
有两个通用 List
实现 - ArrayList
和 LinkedList
。大多数情况下,您可能会使用ArrayList
,它提供恒定时间位置访问,并且速度非常快。它不必为每个元素分配一个节点对象List
,它可以利用System.arraycopy
何时必须同时移动多个元素。想想ArrayList
作为Vector
没有同步开销。
如果您经常在元素的开头添加元素List
或迭代List
从内部删除元素,则应考虑使用LinkedList
。这些操作需要在a LinkedList
和线性时间内的恒定时间ArrayList
。但是你的性能要付出很大的代价。位置访问需要a中的线性时间和a中的LinkedList
恒定时间ArrayList
。此外,常数因素LinkedList
更糟糕。如果您认为要使用a LinkedList
,请在做出选择之前LinkedList
和ArrayList
之前测量应用程序的性能; ArrayList
通常更快。
ArrayList
有一个调整参数 - 初始容量,它指的是ArrayList
在必须增长之前可以容纳的元素数量。LinkedList
没有调整参数和七个可选操作,其中一个是clone
。另外六名addFirst
,getFirst
,removeFirst
,addLast
,getLast
,和removeLast
。LinkedList
还实现了Queue
接口。
CopyOnWriteArrayList
是一个List
由写时复制数组备份的实现。这种实现在性质上类似于CopyOnWriteArraySet
。即使在迭代期间也不需要同步,并且保证迭代器永远不会抛出ConcurrentModificationException
。此实现非常适合维护事件处理程序列表,其中更改很少发生,并且遍历频繁且可能非常耗时。
如果您需要同步,则a Vector
将比ArrayList
同步的稍快Collections.synchronizedList
。但是Vector
有大量的遗留操作,所以要小心总是Vector
使用List
接口操作,否则你将无法在以后替换实现。
有三个专用Map实现- EnumMap
, WeakHashMap
和 IdentityHashMap
。EnumMap
,内部实现为array
,是一个Map
与枚举键一起使用的高性能实现。该实现将Map
接口的丰富性和安全性与接近阵列的速度相结合。如果要将枚举映射到值,则应始终EnumMap
优先使用数组。
WeakHashMap
是一个Map
接口的实现,只存储对其键的弱引用。仅存储弱引用允许键值对在其键不再被引用时被垃圾收集WeakHashMap
。此类提供了利用弱引用功能的最简单方法。它对于实现“类似注册表”的数据结构很有用,其中当任何线程不再可以访问其密钥时,条目的实用程序就会消失。
IdentityHashMap
是Map
基于哈希表的基于身份的实现。此类对于保留拓扑的对象图转换非常有用,例如序列化或深度复制。要执行此类转换,您需要维护一个基于身份的“节点表”,以跟踪已经看到的对象。基于身份的映射还用于维护动态调试器和类似系统中的对象到元信息映射。最后,基于身份的映射可以有效地阻止有意反常equals
方法导致的“欺骗攻击”,因为IdentityHashMap
从不equals
在其密钥上调用该方法。这种实现的另一个好处是它很快。
三个通用 Map
实现是 HashMap
, TreeMap
和 LinkedHashMap
。如果需要SortedMap
操作或按键排序Collection
-view迭代,请使用TreeMap
; 如果你想要最大速度而不关心迭代顺序,请使用HashMap
; 如果您想要接近HashMap
性能和插入顺序迭代,请使用LinkedHashMap
。在这方面,情况Map
类似于Set
。同样,Set Implementations部分中的其他所有内容 也适用于Map
实现。
LinkedHashMap
提供两种不可用的功能LinkedHashSet
。创建时LinkedHashMap
,您可以根据键访问而不是插入来订购它。换句话说,仅查找与键相关联的值会将该键带到地图的末尾。此外,LinkedHashMap
还提供了removeEldestEntry
一种方法,可以重写该方法,以便在将新映射添加到地图时强制执行自动删除过时映射的策略。这使得实现自定义缓存变得非常容易。
例如,此覆盖将允许Map增长到多达100个条目,然后每次添加新条目时它将删除最旧的条目,从而保持100个条目的稳定状态。
private static final int MAX_ENTRIES = 100;
protected boolean removeEldestEntry(Map.Entry eldest){
return size()> MAX_ENTRIES;
}
所述 java.util.concurrent
包中包含的 ConcurrentMap
接口,它延伸Map
与原子putIfAbsent
,remove
和replace
方法,以及 ConcurrentHashMap
该接口的实现。
ConcurrentHashMap
是一个由哈希表备份的高度并发,高性能的实现。执行检索时,此实现永远不会阻塞,并允许客户端选择更新的并发级别。它旨在作为以下内容的替代品Hashtable
:除了实现之外ConcurrentMap
,它还支持所有特有的遗留方法Hashtable
。同样,如果您不需要遗留操作,请小心使用该ConcurrentMap
界面对其进行操作。