java集合框架的实现

集合框架的具体实现

集合框架的实现有如下几类:

  • 通用实现
  • 特殊实现
  • 并发实现
  • 包装实现
  • 简便实现
  • 抽象实现

Set实现

Set实现分为通用和特殊实现

通用实现

通用实现有三种:

  • HashSet
  • TreeSet
  • LinkedHashSet

HashSetTreeSet快的多,它们的时间复杂度大约是O(1) vs O(logn),但是前者是无序的。如果你需要SortedSet接口中的操作,或者要求元素有序的迭代,使用TreeSetLinkedHashSet介于HashSetTreeSet之间,它是链表哈希表实现,提供按插入顺序迭代,并且运行几乎和HasSet一样快。

对于HashSet,需要记住的是,它的迭代时间和条目数及容量有关,是线性的。因此,选择太大的初始容量既浪费空间,也浪费时间。但选择太小的初始容量,也会导致每次扩容时浪费时间在拷贝数据结构上。如果不分配初始容量,默认值是16。手动指定时,建议选择2的幂次,大小则是预期容量增长的2倍,比如:

Set<String> s = new HashSet<>(64);

LinkedHashSet的迭代时间不受容量的影响。

特殊实现

特殊实现有两种:

  • EnumSet 枚举集合
  • CopyOnWriteArraySet 写时复制数组集合

EnumSET对针对枚举类型的高性能集合实现。它提供了一个静态工程方法range支持按范围迭代:

for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
        System.out.println(d);

EnumSet对传统的位标志也提供了一种丰富的,类型安全的替代:

EnumSet.of(Style.BOLD, Style.ITALIC)

CopyOnWriteArraySet是写时复制数组(copy-on-write)备份的集合实现。所有的可变操作,比如addsetremove,都是通过创建一个新的数组拷贝实现的,不需要用锁。即使是迭代,也可以安全的与元素的插入删除同时进行。与大部分集合实现不同的是,它的addremovecontains方法的用时和集合的大小有关。它仅适用于需要频繁迭代但极少修改的集合,比如维护事件处理列表。

List实现

List实现分为通用和特殊实现

通用实现

有两种:

  • ArrayList
  • LinkedList

ArrayList使用的最多,它提供时间复杂度为o(1)的位置访问。它不用为列表中的每个元素分配节点对象,并且在同时移动多个元素时,可以利用System.arraycopy优势。可以将ArrayList当作没有同步的Vector

如果你经常在列表的头部添加元素,或者在迭代时删除元素,可以考虑使用LinkedList,这些操作在LinkedList的时间复杂度是o(1),但是在ArrayList是线性的。但如果是位置访问,则恰好相反。

ArrayList有一个可选的参数,初始容量,LinkedList没有初始容量参数,但是实现了队列接口,支持如下7种可选方法:clone, addFirst, getFirst, removeFirst, addLast, getLast, removeLast

特殊实现
  • CopyOnWriteArrayList

CopyOnWriteArrayList是写时复制数组(copy-on-write)备份的列表实现,和CopyOnWriteArraySet相似。即使在迭代时,也不需要同步,并且迭代器从来不会抛出ConcurrentModificationException异常。这个实现适合维护事件处理列表,这种很少修改,但频繁遍历的情况。

如果你需要同步,那VectorArrayList的同步版Collections.synchronizedList还要快一点,但是它会载入很多过时的操作,因此要小心使用。

如果列表大小固定,也不需要删除、增加或其他批量操作,那么简化实现的Arrays.asList值得考虑。·

###Map实现

分为通用实现,特殊实现,以及并发实现

通用实现

有三种:

  • HashMap
  • TreeMap
  • LinkedHashMap

如果你需要SortedMap接口中的操作,或者按照键有序对集合试图进行迭代,请使用TreeMap;如果你在意速度,并不关心迭代的顺序,请使用HashMap;如果你需要接近HasMap的性能,并且按照插入顺序迭代,请使用LinkedHashMap。从这方面来说,LinkedHashMapLinkedHashSet很像。

LinkedHashMap有两项能力LinkedHashSet不具备。你可以基于key的访问而不是插入来排序,换句话说,只是查找某个key的值,就会导致这个key被重新排序(原文是带到map的末尾)。另外LinkedHashMap提供了removeEldestEntry方法,可以重写该方法来实现插入新的映射时自动移除最旧的映射的策略。利用这点可以很容易实现自定义的缓存。

比如,下面的示例维持map中最多100条映射:

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}
特殊实现

有三种:

  • EnumMap
  • WeakHashMap
  • IdentityHashMap

EnumMap内部是作为数组实现的,它为枚举键的使用提供了高性能的Map实现。它将Map接口的丰富性、安全性与数组的访问速度结合起来。因此,如果你想将枚举映射到值,请优先考虑使用EnumMap

WeakHashMap只存储键的弱引用(不增加引用计数,类似python中弱引用),这样当某个键在WeakHashMap外部不再被引用时,其键值对就会被垃圾回收。WeakHashMap是利用弱引用功能的最简单方式,可用于实现类似注册表这种数据结构,当任何线程都无法访问其键时,条目的实用性就会消失。

IdentityHashMap基于身份的Map实现,比较抽象,暂不讨论。

并发实现

一种:

  • ConcurrentHashMap

java.util.concurrent包中的ConcurrentMap接口,有原子性的putIfAbsent, remove, replace方法,ConcurrentHashMap实现了这个接口。

ConcurrentHashMap是由哈希表支持的高并发,高性能实现。执行检索时它不会阻塞,并且允许客户端选择更新的并发级别。它用于替代Hashtable,除了实现了ConcurrentMap接口,它支持Hashtable中所有的旧方法。但是不建议继续调用这些过时的方法。

Queue实现

分为通用实现和并发实现

通用实现

两种:

  • LinkedList
  • PriorityQueue

LinkedList实现了Queue接口,提供先入先出的队列操作。

PriorityQueue是基于堆这种数据结构的优先级队列。它根据构造时指定的顺序(可以是自然排序,或Comparator对象),对元素进行排序。

队列的检索操作:poll, remove, peek, 和element,访问队列头部的元素,也就是排序的最小值。如果多个元素都是最小值,那么头部是其中某个元素。

PriorityQueue及它的迭代器实现了CollectionIterator接口中所有可选可选方法。但是迭代器不保证以特定的顺序遍历PriorityQueue中的元素。如果需要有序迭代,可以考虑使用Arrays.sort(pq.toArray())

并发实现

java.util.concurrent包含许多同步的(线程安全的)队列接口和类。BlockingQueue接口实现了检索元素时,等待队列非空,存储元素时,等待空间可用,下面的这些类实现了该接口:

  • LinkedBlockingQueue 链表实现的FIFO队列
  • ArrayBlockingQueue 数组实现的FIFO队列
  • PriorityBlockingQueue 堆实现的优先级队列
  • DelayQueue 堆实现的基于时间的调度队列
  • SynchronousQueue 使用BlockingQueue接口的简单机制(不明所以)

Deque实现

分为通用实现和并发实现

通用实现

两种:

  • LinkedList
  • ArrayDeque

Deque接口支持从两端插入、移除及检索元素,它的基本方法是:addFirst, addLast, removeFirst, removeLast, getFirst, getLast

LinkedList更灵活,实现了列表的所有可选操作,并且允许null元素。LinkedList最适合的操作是迭代时移除元素。但它消耗更多内存。

ArrayDeque是可调整大小的数组实现,如果考虑效率的话,ArrayDeque的插入和移除操作效率更高。

可以通过两种方式便利ArrayDeque:

foreach
ArrayDeque<String> aDeque = new ArrayDeque<String>();

. . .
for (String str : aDeque) {
    System.out.println(str);
}
Iterator
ArrayDeque<String> aDeque = new ArrayDeque<String>();
. . .
for (Iterator<String> iter = aDeque.iterator(); iter.hasNext();  ) {
    System.out.println(iter.next());
}
并发实现

一种:

  • LinkedBlockingDeque

如果双向队列为空,方法takeFirst, takeLast会等待,直到元素可用。

包装实现

包装实现将任务委派给具体的集合,但是会添加一些额外的功能。按设计模式来说,它算是装饰器模式。包装实现是匿名的,它们提供静态工厂方法,而不是公众类。你可以在Collections类中找到这些实现。

同步包装

六个核心集合接口:Collection, Set, List, Map, SortedSet, SortedMap,都有静态工厂方法:

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

每个静态工厂方法都返回一个同步的(线程安全的)集合。为了确保顺序访问,对原集合的所有访问都必须通过这个返回的集合进行。确保这一点的最容易的方式就是,不要保留原来的集合引用,比如,我们可以用下面这个方式创建一个同步集合:

List<Type> list = Collections.synchronizedList(new ArrayList<Type>());

在面对并发访问时, 用户必须手动同步正在迭代的同步集合。因为迭代通过多次调用集合完成,这必须被整合为一个原子性操作。下面是迭代一个同步的包装集合的惯用方式:

Collection<Type> c = Collections.synchronizedCollection(myCollection);
synchronized(c) {
    for (Type e : c)
        foo(e);
}

注意,这里使用了synchronized关键字来手动同步,迭代必须在synchronized代码块内进行,否则会导致不可预料的行为。迭代集合的视图与此类似,也需要手动同步,比如:

Map<KeyType, ValType> m = Collections.synchronizedMap(new HashMap<KeyType, ValType>());
    ...
Set<KeyType> s = m.keySet();
    ...
// Synchronizing on m, not s!
synchronized(m) {
    while (KeyType k : s)  // 使用while进行迭代
        foo(k);
}

注意,手动同步是在同步的Map上进行,而不是它的视图。

不可修改包装

不可修改包装会拦截对集合的修改操作,并抛出UnsupportedOperationException异常。它们的两种主要作用是:

  • 使集合一旦创建便不可修改
  • 允许特定的客户端以只读模式访问你的数据结构。你仍然可以保持对原集合的引用,但是暴露包装的引用。通过这种方式,客户端只能看不能改,而你拥有完全的访问权限。

和同步包装类似,六个核心的集合接口都有静态工厂方法:

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

Collections.checked接口包装用于泛型集合。它们返回指定集合的动态类型安全的视图。如果客户端尝试添加错误类型的元素,视图会抛出ClassCastException异常。java的泛型机制提供了编译时的类型检查,但是这一机制还是可能会被打破。而动态类型安全视图可以彻底干掉这种可能。

简便实现

如果你不需要通用实现的全部能力,那么简便实现更方便,更高效。

数组的列表视图

Arrays.asList 方法返回其数组参数的列表视图。对列表的修改会写回数组,反之也一样。列表的大小固定,等于数组的长度。对列表调用addremove方法,会抛出UnsupportedOperationException异常。

列表视图是基于数组的接口与基于集合的接口之间的桥梁。它允许你传一个数组给一个期望集合的方法。另外,它的还有一个用处,如果你需要一个定长的列表,那么它比起通用实现更高效。下面是它的惯用法:

List<String> list = Arrays.asList(new String[size]);
不可变重复元素列表

偶尔你需要一个不可变的列表,它包含同样的元素的多次重复。Collections.nCopies方法就返回这样一个列表。它有两种用途,一是用来初始化一个刚创建的列表。比如,你想要一个包含一百个1元素的列表,你可以这样做:

List<Integer> list = new ArrayList<>(Collections.nCopies(100, 1));

第二种用法是扩充一个列表。假如你想将上面的列表再添加100个2进去,你可以这样做:

list.addAll(Collections.nCopies(100, 2));
不可变单元素集合

有时候你可能需要一个不可变的Set,且它只包含单个指定的元素。Collections.singleton方法就返回这样一个Set。它的一个用法是从集合中移除指定元素的所有出现,比如还是上面的列表,将刚添加进去的2全部移除

list.removeAll(Collections.singleton(2));

另一个惯用法是从一个Map中移除某些value值一样的所有元素。比如你有个Map,它记录每个人与其职业的对应关系,现在你想移除所有的律师,你可以这样做:

job.values().removeAll(Collections.singleton(LAWYER));

还有一个用法是给接收集合的方法提供单个值。

空Set, List, Map常量

emptySet, emptyList, emptyMap方法返回对应类型的空集合。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值