在Java中遍历List的最佳方法是什么?

时间:2013-02-26 14:09:46

标签: java performance synchronization

我有一个在多线程环境中使用的并发List。构建List后,大多数操作都会遍历它。我想知道以下哪两种方法更有效,或者创建一个新的List vs使用synchronized的成本是多少?或者还有其他更好的方法?

List<Object> list = new CopyOnWriteArrayList<Object>();

public int[] getAllValue1() {
    List<Object> list2 = new ArrayList<Object>(list);
    int[] data = new int[list2.size()];
    int i = 0;
    for (Object obj : list2) {
        data[i++] = obj.getValue();
    }
    return data;
}

public int[] getAllValue2() {
    synchronized (list) {
        int[] data = new int[list.size()];
        int i = 0;
        for (Object obj : list) {
            data[i++] = obj.getValue();
        }
        return data;
    }
}

更新 getAllValue1():它是线程安全的,因为它需要CopyOnWriteList的快照,它本身就是线程安全List。然而,正如Sharakan指出的那样,成本是迭代2列表,并创建一个本地对象ArrayList,如果原始列表很大,这可能是昂贵的。

getAllValue2():它在同步块中也是线程安全的。 (假设其他函数正确地进行同步。)将它放入同步块的原因是因为我想预先分配数组,以确保.size()调用与迭代同步。 (迭代部分是线程安全的,因为它使用CopyOnWriteList。)但是这里的成本是使用syncrhonized块的机会成本。如果有100万个客户端调用getAllValue2(),则每个客户端都必须等待。

所以我猜答案实际上取决于有多少并发用户需要读取数据。如果并发用户不多,那么方法2可能更好。否则,method1更好。同意?

在我的使用中,我有几个并发客户端,可能method2是首选。 (顺便说一句,我的名单大约是10k)。

3 个答案:

答案 0 :(得分:0)

修改(再次): 因为你在写数组列表上使用了一个副本(应该更加观察),我会得到迭代器并使用它来初始化你的数组。由于迭代器是您请求它时数组的快照,因此您可以在列表上进行同步以获取大小,然后进行迭代而不必担心ConcurrentModificationException或更改迭代器。

public int[] getAllValue1() {
    synchronized(list){
        int[] data = new int[list.size()];            
    }
    Iterator i = list.iterator();
    while(i.hasNext()){
        data[i++] = i.next().getValue();
    }
    return data;
}

答案 1 :(得分:0)

getAllValue1看起来很好,因为你需要根据对象的一个​​字段返回一个基本类型数组。它将是两次迭代,但是一致并且您不会在读取器线程之间引起任何争用。您尚未发布任何分析结果,但除非您的列表非常大,否则我会更担心多线程环境中的争用而不是两次完整迭代的成本。

如果更改API,则可以删除一次迭代。最简单的方法是返回一个Collection,如下所示:

public Collection<Integer> getAllValue1() {
    List<Integer> list2 = new ArrayList<Integer>(list.size());
    for (Object obj : list2) {
        list2.add(obj.getValue());
    }
    return list2;
}

如果您可以通过这种方式更改API,那将是一项改进。

答案 2 :(得分:0)

我认为第二个更有效率。原因是,在第一个中,您创建另一个列表作为本地创建。这意味着如果原始列表包含大量数据,它将复制所有数据。如果它包含数百万个数据,那么这将是一个问题。

但是,有list.toArray()方法

Collections界面还包含一些有用的东西

  Collection synchronizedCollection = Collections.synchronizedCollection(list);

List synchronizedList = Collections.synchronizedList(list);

如果您需要VALUE对象,而不是对象,请使用您的第二个代码移动。另外,你可以用上面的函数替换第二个代码的适当部分,并做任何你想做的事。