10

要素の Collection を作成する必要があると想像してください。順序が重要であるかどうかは関係ありません。私が実際に計画しているのは、イテレータを使用することだけです。ほとんどの同僚が ArrayList と LinkedHashSet/HashSet を使用していることに気付きました。私の質問は、これらの要素が一意であることがわかっている場合、セットまたはリストを使用する必要があるかどうかです。効果的には違いはありませんが、要素が一意であることを Set の方が効果的に伝えていませんか?

いくつかの理由から、これは大規模なエンタープライズ アプリケーションにとって興味深い質問だと思います。1) コード全体の品質を保証できない場合、Set の使用は危険な場合があります。なんで?equals() と hashcode が誤ってオーバーライドされる可能性があるため、 Set を使用すると、非常に厄介な問題が発生する可能性があります。2) リストを使用すると、将来の変更に対する回復力が高くなります。なんらかの理由で複製が可能になったとしても、心配する必要はありません。

基本的には、次のようになります。一意の要素を期待する必要があることがわかっている場合、すべての場合にリストよりもセットを優先する必要がありますか?

編集:私も尋ねていると思います:重複が追加されないようにするために Set を使用する必要がありますか、または理解を容易にするために重複が存在しないことを示すためだけに使用できますか?

4

10 に答える 10

7

1) 完全に嘘です。バグを回避するのではなく、修正してください。したがって、順序が重要でない場合は任意のSet実装を使用し、順序重要な場合はSortedSetを使用します。要素が一意である必要がない場合 (そして、今それを決定する必要があり、通常は変更されるべきではありません)、自由にListを使用してください。

于 2009-06-17T08:39:21.040 に答える
2

独自の要素を考える必要がある場合は、Set を使用します。しかし、ユーザーが equals/hashCode を適切に実装することを信頼していない場合は、イテレーションに何か問題がある場合は、equals/hashCode を確認することを文書化することをお勧めします! しかし、実際にはデータ モデルのユース ケースに依存します。

于 2009-06-17T08:40:17.653 に答える
1

誰かが、HashSet は追加、削除、包含、およびサイズに関して一定時間のパフォーマンスを提供すると言いました。

JavaDocs の実際のステートメントは、「このクラスは、ハッシュ関数が要素をバケット間で適切に分散すると仮定すると、基本的な操作 (追加、削除、含む、およびサイズ) に対して一定時間のパフォーマンスを提供します。」

これは、hashCode メソッドの実装が不十分な場合、セットに何かを追加するときに追加時間が遅くなる可能性があることを意味します。

次のコードは、hashCode の実装に応じて何が起こるかを示しています。

public void testHashSetAddition() {
    for(int mod=10; mod <= 100; mod=mod+10 ) {
        Set s = new HashSet();
        long start = new Date().getTime();
        for(int i=0; i<100000; i++) {
            s.add(new Foo(i % mod));
        }
        long end = new Date().getTime();
        System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
    }
}

class Foo {
    private int hc;
    public Foo(int i) {
        this.hc = i;
    }
    public int hashCode() {
        return hc;
    }
}

タイミングの結果は次のとおりです。

Mod: 10 - 22683ms
Mod: 20 - 14200ms
Mod: 30 - 10486ms
Mod: 40 - 8562ms
Mod: 50 - 7761ms
Mod: 60 - 6740ms
Mod: 70 - 5778ms
Mod: 80 - 5268ms
Mod: 90 - 4716ms
Mod: 100 - 3966ms

次に、ArrayList に対してまったく同じテストを行います。

public void testAddingToArrayList() {
    for(int mod=100; mod >= 10; mod=mod-10 ) {
        List l = new ArrayList();
        long start = new Date().getTime();
        for(int i=0; i<100000; i++) {
            l.add(new Foo(i % mod));
        }
        long end = new Date().getTime();
        System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
    }
}

与えます:

Mod: 100 - 50ms
Mod: 90 - 30ms
Mod: 80 - 40ms
Mod: 70 - 30ms
Mod: 60 - 30ms
Mod: 50 - 40ms
Mod: 40 - 20ms
Mod: 30 - 30ms
Mod: 20 - 30ms
Mod: 10 - 30ms
于 2009-06-17T14:56:48.907 に答える
1
    import java.util.*;

    public class Test {
        public void testHashSetAddition() {
            for(int mod=10; mod <= 100; mod=mod+10 ) {
                Set s = new HashSet();
                long start = new Date().getTime();
                for(int i=0; i<100000; i++) {
                    s.add(new Foo(i % mod));
                }
                System.out.println(s.size());
                long end = new Date().getTime();
                System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
            }
        }
        public void testAddingToArrayList() {
            for(int mod=100; mod >= 10; mod=mod-10 ) {
                List l = new ArrayList();
                long start = new Date().getTime();
                for(int i=0; i<100000; i++) {
                    l.add(new Foo(i % mod));
                }
                System.out.println(l.size());
                long end = new Date().getTime();
                System.out.println("Mod: " + mod + " - " + (end - start) + "ms");
            }
        }

        public static void main(String...a){
            new Test().testHashSetAddition();
            new Test().testAddingToArrayList();
        }
        class Foo {
            private int hc;
            public Foo(int i) {
                this.hc = i;
            }
            public int hashCode() {
                return hc;
            }
            public int getHc(){
                return hc;
            }
            public boolean equals(Object o){
                if(!(o instanceof Foo)) return false;
                Foo fo = (Foo)o;
                return fo.getHc() == this.hc;
            }
        }

    }
/*
10
Mod: 10 - 31ms
20
Mod: 20 - 16ms
30
Mod: 30 - 15ms
40
Mod: 40 - 16ms
50
Mod: 50 - 0ms
60
Mod: 60 - 16ms
70
Mod: 70 - 0ms
80
Mod: 80 - 15ms
90
Mod: 90 - 0ms
100
Mod: 100 - 0ms
100000
Mod: 100 - 32ms
100000
Mod: 90 - 31ms
100000
Mod: 80 - 31ms
100000
Mod: 70 - 31ms
100000
Mod: 60 - 32ms
100000
Mod: 50 - 15ms
100000
Mod: 40 - 31ms
100000
Mod: 30 - 32ms
100000
Mod: 20 - 15ms
100000
Mod: 10 - 32ms
*/
于 2012-06-07T03:11:55.310 に答える
0

@Andrzej Doyle-セットに要素を追加すると、重複した比較が行われるとは思いません。セットは内部でhashMapを使用するため、重複するキーはオーバーライドされ、特定のチェックは行われません。

于 2011-05-10T10:20:37.747 に答える
0

どちらの選択も意図を伝えるために考慮されるべきではないと思います-あなたのメソッドはCollection、柔軟性のために、そしてあなたが言ったように、それの消費者はただ繰り返すことができるはずなので、適切なジェネリックパラメータで単にを返すように宣言されるべきですそれがどんなタイプであるかを気にせずにそれの上に。これにより、要件が後で変更された場合、または何らかの理由で最初の選択が間違っていたことが判明した場合に、コードを1か所で変更する必要があるという追加の利点が得られます(最初のコンストラクター呼び出し)。

意図は、メソッドのドキュメントで指定する必要があります。このドキュメントでは、コレクションのイテレータが特定の順序で要素を返すかどうか、および重複する要素が表示されるかどうかを詳しく説明する必要があります。

また、ポイント1)に関する推論がオフであるという上記の投稿にも同意します。実装が正しくないクラスequalshashcodeセットに入れたいクラスがある場合は、それらを修正してからセットを使用します。

于 2009-06-17T09:16:37.800 に答える
0

一意性を強制し、どこが間違っているかを示すため、必要に応じて設定します。

メソッドが誤ってオーバーライドされた場合、いくつかの問題が発生する可能性がありますが、正しい選択は祈りをせず、メソッドの呼び出しを避けることです。バグを検出して修正します。

編集:そして、はい、セットを見るとより明確になります。一意の値が必要であり、さらに優れています:一意の値が適用されます。コードの使用法について推測したり信頼したりしないでください;)

于 2009-06-17T08:38:49.513 に答える
0

@Andrzej Doyle - 要素をセットに追加すると、重複した比較が行われるとは思わない

于 2011-05-10T10:17:33.863 に答える