【问题标题】:Why is Collections.synchronizedList(list) internally using the instanceof check?为什么 Collections.synchronizedList(list) 在内部使用 instanceof 检查?
【发布时间】:2017-12-13 10:55:36
【问题描述】:

我正在查看 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。 那么这个检查有什么意义呢? 请解释

【问题讨论】:

  • String 继承 Object ... 你认为这意味着应该没有 String 类吗?
  • 因为如果特定的List 实现实现了标记接口RandomAccess,它应该返回SynchronizedRandomAccessList,这对于提供高效随机访问的列表实现可能更有效;如果列表不提供随机访问,则应使用SynchronizedList
  • @Stultuske 我的问题是我们为什么要使用instanceof检查
  • @RahulMathur 代码很清楚,不是吗?因为他们想知道是否应该将列表解析为使用随机访问的列表,还是不使用随机访问的列表

标签: java collections java-8


【解决方案1】:

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

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

那么,Collections.synchronizedList(list) 发生了什么?它将RandomAccessList-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()

【讨论】:

  • 没有不同的锁定模型。正如方法名称所暗示的和the documentation 指定的那样,两者都使用synchronized
  • 我使用了非特定的措辞。 SynchronizedRandomAccessListSynchronizedList 这两个类对锁的管理方式不同。这就是我所指的。
  • 对于该主题的专家来说,这是一个很好的问题 :) 你看,它们是不同的类。我认为这背后有充分的理由。我的意思是编写 jvm 代码的专家,他们决定用不同的类包装这两种列表。
【解决方案2】:

您必须回想RandomAccess 标记界面的最初用途。如果您将List 传递给另一个方法,它应该能够选择适合随机访问或顺序列表的算法。选择正确的算法需要通过list instanceof RandomAccess 测试标记接口。

显示one example

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,以便接收这种包装器的方法仍然能够检测快速随机访问是否可用。

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

→ 是的

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

→ 错误

【讨论】:

  • 基本上是一种区分方式并因此选择更好的方法?
  • @Eugene:是的。如果从一开始就考虑到这一点,即在 Java 1.2 中,那么它可能会产生一个方法 List.isRandomAccess(),它可以通过委托给源列表来简单地实现。不幸的是,这个问题被忽略了,在 1.4 中添加了 RandomAccess 标记接口(如果在 1.8 中添加了,default 方法是一个选项)。
  • 阅读您的答案后,我似乎从来没有对任何问题提出足够的质疑。谢谢你
猜你喜欢
  • 2013-09-18
  • 1970-01-01
  • 2011-02-05
  • 2013-08-06
  • 1970-01-01
  • 2023-04-10
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多