Java 集合框架知识梳理


RYFvUU19FiNrJ9

您是否注意到上图中的152桶?我们发现John Smith和Sandra Dee Keys都指向同一个桶,也就是说,两个对象的hashCodes是相等的。这是equals和hashCode之间关系的第二点。 Java使用LinkedList来存储所有Key hashCodes的键值对。

此时,在LinkedList中,前一个键值对保持指向下一个键值对的指针。当您稍后通过John Smith获取值时,Java首先发现在其相应的存储桶中有两个键值对。然后,Java将每个键值对中的关键对象与传输的Key对象进行比较(如果它们相等)。返回与此键值对对应的值。

因此,我们也知道为什么HashMap需要存储Key和Value而不仅仅是Value。它也知道合同第二点的原因。实际上,我们可以将类的所有对象返回到相同的hashCode,但是如果类的对象用作Key,则每次获取时,所有键值对都将存储相同的存储桶位置。需要进行多重比较,因此会影响HashMap采集的速度。

线程安全性

通常,有两种方法可以在Java中实现线程安全,一种是通过Java自己的并发控制(例如使用syncronized关键字),另一种是通过创建不可变对象。

在早期,Java有两个集合对象,Vector和HashTable。他们使用syncronized关键字实现了线程安全,但是他们也暴露了很多性能问题。所以现在基本上没有人使用它。为了解决Vector和HashTable的性能问题,从1.2引入的Java集合框架采用了线程不安全的类。例如,ArrayList和HashMap是线程不安全的。

当然,不建议牺牲线程安全性来提高性能。因此,Java使用Fail-Fast的Iterator来避免由多个线程同时运行引起的线程冲突。例如,当一个线程遍历一个集合而另一个线程正在修改集合时,前者将抛出ConcurrentModificationException。这当然不是一个完美的解决方案。

另一方面,Java实际上提供了一个线程安全的包装器来实现集合的线程安全性。我们可以通过:

Collections.synchronizedXXX(集合)

要创建线程安全的集合,其中XXX可以是Collection,List,Map和Set。对于常规的colleciton对象,调用(synchronizedXXX)方法将导致封装的线程安全性。

集合包装器类还使用syncronized关键字来实现线程安全,并且在整个集合类中被锁定,这也会导致严重的性能问题。为了解决这些问题,自Java 5以来引入了Concurrent Collections。它们使用Immutable集合或使用更复杂的锁定控制来实现线程安全,同时确保高性能。

并发集合主要由三个类别组成,一个是Copy-On-Write集合,第二个是Compare-And-Swap集合,第三个是带有特殊锁的并发集合。 Copy-On-Write集合的底层通过在将新集合写入集合时重新复制新集合来维护一个线程安全的不可变数组,因此名称为Copy-On。 -写。

Coppy-On-Write集合包括CopyOnWriteArrayList和CopyOnWriteArraySet。更新Compare-And-Swap集合时,它首先维护本地副本。执行更新时,将本地副本与原始值进行比较。如果值相等,则证明在此期间没有其他线程修改了原始值。它会立即更新;如果它不相等,则重新复制原始值,然后计算,然后更新,这也是线程安全的目的。 Compare-And-Swap集合包括ConcurrentLinkedQueue和ConcurrentSkipListMap。

第三种类型是特殊锁的集合。此集合类不会锁定整个集合类,而是将其锁定在存储桶级别,从而实现对并发性的更好控制并减少线程延迟。这提高了并发性能。

除了提供并发控制机制之外,Java还提供了不可修改的集合以确保线程安全,可以是:

Collections.unmodifiableXXX(集合)

创建不同的不可修改的集合。这样的集合实际上是一个包装类。对于传入的普通集合集合,add方法抛出unsupportedOperationException以实现不可修改的目的。但是,这样的集合不能实现不可修改的目的,因为集合本身仍然是可修改的。

为了实现更好的集合不变性,Guava类库提供了一组真正不变的Immutable。

如果您喜欢这篇文章,请转发并喜欢它。注意订阅号“Web Project Gathering Place”并回复“Technology Blog”以获取更多图形教程,技术博客文章和学习资源。