0

マルチスレッド環境で使用される並行リストがあります。List が構築されると、ほとんどの操作はそれを走査します。次の2つの方法のどちらがより効率的か、または同期を使用する場合と比較して新しいリストを作成するコストはいくらですか? それとも他にもっと良い方法があるのでしょうか?

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;
    }
}

UPDATE getAllValue1(): それ自体がスレッドセーフなリストである CopyOnWriteList のスナップショットを取得するため、スレッドセーフです。ただし、sharakan が指摘しているように、コストは 2 つのリストを反復し、ローカル オブジェクト ArrayList を作成することであり、元のリストが大きい場合はコストがかかる可能性があります。

getAllValue2(): 同期ブロックでもスレッドセーフです。(他の関数が適切に同期を行うと仮定します。) 同期ブロックに配置する理由は、.size() 呼び出しが反復と確実に同期されるように、配列を事前に割り当てたいためです。(反復部分は CopyOnWriteList を使用するため、スレッドセーフです。) ただし、ここでのコストは、同期ブロックを使用する機会コストです。getAllValue2() を呼び出すクライアントが 100 万ある場合、それぞれが待機する必要があります。

したがって、答えは、データを読み取る必要がある同時ユーザーの数に大きく依存すると思います。多くの同時ユーザーがいない場合は、おそらく method2 の方が優れています。それ以外の場合は、method1 の方が適しています。同意?

私の使用法では、いくつかの同時クライアントがあり、おそらく method2 が優先されます。(ところで、私のリストは約 10k サイズです)。

4

3 に答える 3

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;
}
于 2013-02-26T14:15:56.353 に答える
0

オブジェクトのフィールドに基づいてプリミティブ型の配列を返す必要がある場合、getAllValue1 は適切に見えます。2 回の繰り返しになりますが、一貫性があり、リーダー スレッド間で競合が発生することはありません。プロファイリング結果を投稿していませんが、リストが非常に大きくない限り、2 回の完全な反復のコストよりも、マルチスレッド環境での競合のほうが心配です。

API を変更すると、反復を 1 つ削除できます。これを行う最も簡単な方法は、次のように代わりに Collection を返すことです。

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

APIをそのように変更できれば、それは改善になります。

于 2013-02-26T14:23:44.717 に答える
0

2番目の方が効率的だと思います。その理由は、最初のリストでは、ローカル作成として別のリストを作成するためです。つまり、元のリストに大量のデータが含まれている場合、それらすべてがコピーされます。何百万ものデータが含まれている場合、それは問題になります。

ただしlist.toArray()方法はある

コレクションインターフェースには、いくつかの便利なものも含まれています

  Collection synchronizedCollection = Collections.synchronizedCollection(list);

また

List synchronizedList = Collections.synchronizedList(list);

オブジェクトではなく、オブジェクト VALUE が必要な場合は、2 番目のコードに移動します。それ以外の場合は、2 番目のコードの適切な部分を上記の関数に置き換えて、好きなことを行うことができます。

于 2013-02-26T14:23:49.247 に答える