我正在查看收藏集类的源代码。 我遇到了方法 Collections.synchronizedList(list)
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
我无法理解为什么我们要检查列表是否属于RandomAccess类型。 我知道ArrayList实现了这个接口,而LinkedList没有。
此外, SynchronizedRandomAccessList 会继承 SynchronizedList 。 那检查有什么意义呢? 请解释
答案 0 :(得分:5)
你几乎就在那里。 Collections.synchronizedList(list)
检查RandomAccess
,因为有些列表会实施RandomAccess
,而其他列表则不会。
此RandomAccess
是marker interface,就像Serializable
一样。它告诉我们是否可以随机访问列表中的项目。即从ArrayList
我们可以检索具有相同计算成本的任何项目。另一方面,我们需要遍历LinkedList
才能到达第n个元素。
那么,Collections.synchronizedList(list)
发生了什么?它将RandomAccess
List
- s包装到RandomAccess
同步列表中,同时将非RandomAccess
列表包装到非RandomAccess
同步列表中。否则这些列表是相同的。以下是SynchronizedRandomAccessList
的代码。这是programming by difference的一个很好的例子。这两个类几乎相同。
static class SynchronizedRandomAccessList<E>
extends SynchronizedList<E>
implements RandomAccess {
SynchronizedRandomAccessList(List<E> list) {
super(list);
}
SynchronizedRandomAccessList(List<E> list, Object mutex) {
super(list, mutex);
}
public List<E> subList(int fromIndex, int toIndex) {
synchronized (mutex) {
return new SynchronizedRandomAccessList<>(
list.subList(fromIndex, toIndex), mutex);
}
}
private static final long serialVersionUID = 1530674583602358482L;
/**
* Allows instances to be deserialized in pre-1.4 JREs (which do
* not have SynchronizedRandomAccessList). SynchronizedList has
* a readResolve method that inverts this transformation upon
* deserialization.
*/
private Object writeReplace() {
return new SynchronizedList<>(list);
}
}
您可能会问,RandomAccess
界面的重点是什么?正如Holger所指出的,Collections.binarySearch()
基于此接口做出决策。另一个例子是Collections.reverse()
。
答案 1 :(得分:3)
您必须回忆RandomAccess
标记界面的原始用途。如果将List
传递给另一个方法,它应该能够选择适合随机访问或顺序列表的算法。选择正确的算法需要通过list instanceof RandomAccess
测试标记接口。
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
(另请参阅reverse
,shuffle
,copy
和fill
)
现在,如果您只是将列表包装到另一个实现List
接口的对象中,那么所有这些方法都会看到包装器对象,因此这些信息会丢失。但是,像同步列表这样的包装器不会改变随机访问方法(如get
)的时间复杂度。因此,如果包装列表是随机访问列表,则期望包装器也应该实现RandomAccess
,以便接收这种包装器的方法仍然能够检测快速随机访问是否可用。
如果查看implementation of SynchronizedRandomAccessList
,您会看到它所做的全部工作是扩展SynchronizedList
并实施RandomAccess
,以继承行为并将自己标记为具有快速随机访问权限。它覆盖的唯一方法是subList
,原因完全相同。如果列表具有有效的随机访问权限,则其子列表也具有,因此它们也应该实现RandomAccess
。
static class SynchronizedRandomAccessList<E>
extends SynchronizedList<E>
implements RandomAccess {
SynchronizedRandomAccessList(List<E> list) {
super(list);
}
SynchronizedRandomAccessList(List<E> list, Object mutex) {
super(list, mutex);
}
public List<E> subList(int fromIndex, int toIndex) {
synchronized (mutex) {
return new SynchronizedRandomAccessList<>(
list.subList(fromIndex, toIndex), mutex);
}
}
请注意,其他包装工厂(如checkedList
)遵循相同的模式。所以这甚至可以在组合工厂时起作用:
System.out.println(
Collections.synchronizedList(Collections.checkedList(new ArrayList<>(), String.class))
instanceof RandomAccess);
→true
System.out.println(
Collections.synchronizedList(Collections.checkedList(new LinkedList<>(), String.class))
instanceof RandomAccess);
→false