繁体   English   中英

为什么Collections.synchronizedList(list)在内部使用instanceof检查?

[英]Why is Collections.synchronizedList(list) internally using the instanceof check?

我正在检查Collections类的源代码。 我遇到了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 那检查的重点是什么? 请解释

你快到了。 Collections.synchronizedList(list)检查是否有RandomAccess因为某些列表实现了RandomAccess而另一些则没有。

就像Serializable一样,此RandomAccess标记接口 它告诉我们是否可以随机访问列表中的项目。 即从ArrayList我们可以检索具有相同计算成本的任何项目。 另一方面,我们需要遍历LinkedList才能到达第n个元素。

那么, Collections.synchronizedList(list)发生了什么? 它将RandomAccess List -s包装到RandomAccess同步列表中,而将非RandomAccess列表包装到非RandomAccess同步列表中。 否则,这些列表是相同的。 以下是SynchronizedRandomAccessList的代码。 这是通过差异编程的一个很好的例子。 这两类几乎是相同的。

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()

您必须回顾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);
 }

(另请参见reverseshufflecopyfill

现在,如果仅将列表包装到实现List接口的另一个对象中,则所有此类方法都是包装对象,因此此信息会丢失。 但是,包装程序(如同步列表)不会更改诸如get类的随机访问方法的时间复杂度。 因此,期望如果包装列表是随机访问列表,则包装器也应该实现RandomAccess ,以便接收这种包装器的方法仍然能够检测快速随机访问是否可用。

如果查看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);

→正确

System.out.println(
    Collections.synchronizedList(Collections.checkedList(new LinkedList<>(), String.class))
    instanceof RandomAccess);

→错误

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM