Javaで一意のリストを維持する方法は?


104

Javaで一意/個別オブジェクト(重複なし)のリストを作成する方法は?

現在、私はHashMap<String, Integer>これを行うために使用しています。これは、キーが上書きされるため、最後にHashMap.getKeySet()一意になるキーを取得できるためです。しかし、ここでは価値の部分が無駄になっているので、これを行うにはもっと良い方法があるはずだと私は確信しています。

回答:


164

Set実装を使用できます。

JAVADocからの情報:

重複する要素を含まないコレクション。より正式には、セットにはe1.equals(e2)のような要素e1とe2のペアが含まれず、最大で1つのnull要素が含まれます。その名前が示すように、このインターフェースは数学セットの抽象化をモデル化しています。

注:変更可能なオブジェクトをセット要素として使用する場合は、細心の注意を払う必要があります。オブジェクトがセット内の要素であるときに、等しい比較に影響する方法でオブジェクトの値が変更された場合、セットの動作は指定されません。この禁止事項の特別なケースは、セットがそれ自体を要素として含むことは許可されないことです。

これらは実装です:

  • ハッシュセット

    このクラスは、ハッシュ関数がバケット間で要素を適切に分散すると仮定して、基本的な操作(追加、削除、包含、サイズ)に対して一定の時間パフォーマンスを提供します。このセットを反復処理するには、HashSetインスタンスのサイズ(要素の数)とバッキングHashMapインスタンスの「容量」の合計(バケット数)に比例した時間が必要です。したがって、反復のパフォーマンスが重要な場合は、初期容量を高くしすぎないように(または負荷係数を低くしすぎないように)設定することが非常に重要です。

    aを反復するときHashSet、生成された要素の順序は定義されていません。

  • LinkedHashSet

    ハッシュテーブルとリンクリストのSetインターフェースの実装で、予測可能な反復順序。この実装は、すべてのエントリで実行される二重リンクリストを維持するという点でHashSetとは異なります。このリンクされたリストは、要素がセットに挿入された順序(挿入順序)である反復順序を定義します。要素がセットに再挿入されても、挿入順序は影響を受けないことに注意してください。(s.contains(e)が呼び出しの直前にtrueを返すときにs.add(e)が呼び出された場合、要素eはセットsに再挿入されます。

    だから、上のコードの出力...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ...必然的に

    3
    1
    2
  • TreeSet

    この実装は、基本的な操作(追加、削除、包含)に保証されたlog(n)時間コストを提供します。デフォルトでは、反復で返される要素は「自然順序付け」でソートされているため、上記のコードは...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ...これを出力します:

    1
    2
    3

    ComparatorインスタンスをTreeSetコンストラクタに渡して、要素を異なる順序でソートすることもできます。)

    セットによって維持される順序付け(明示的なコンパレーターが提供されているかどうかにかかわらず)は、Setインターフェースを正しく実装するためには、equalsと一致している必要があります。(equalsとの一貫性の正確な定義については、ComparableまたはComparatorを参照してください。)これは、Setインターフェースがequals操作に関して定義されているためですが、TreeSetインスタンスは、compareTo(またはcompare)メソッドを使用してすべての要素の比較を実行するため、2つのこの方法で等しいと見なされる要素は、セットの観点からは等しいです。セットの順序は、equalsと矛盾しても、セットの動作は明確に定義されています。Setインターフェースの一般的な規約に従わないだけです。


今、私は混乱しています、どちらを使用しますか?一意の文字列のリストを保持する必要があるだけです。そのため、基本的に、既存の文字列が追加された場合でも、実際に追加されるはずです。

1
選択はあなた次第です... HashSetは普遍的で高速であり、ツリーセットは順序付けされ、LinkedHashsetは挿入順序を維持します...
Frank

6
これはLISTではありません...したがって、すべてのLISTインターフェースメソッドを使用できるわけではありません。
marcolopes 2016年

2
セットはリストではありません。O(1)時間(ランダムアクセス)では、セット内の要素をインデックスで検索できません。
ウィルモル

13

元のポスターについて、他の人がほのめかしてはいるが明確には述べていないことをここで明確にしたいと思います。一意のリストが必要だと言うとき、それが順序付きセットのまさに定義です。SetインターフェースとListインターフェースのその他の主な違いは、Listでは挿入インデックスを指定できることです。それで、質問は本当にリストインターフェースが必要ですか?また、インターフェイスで何をしているのかを考慮する必要があります。インデックスで要素を見つけることは重要ですか?セットにはいくつの要素が必要ですか?あなたが多くの要素を持つつもりなら、注文は重要ですか?

本当に一意の制約があるリストが本当に必要な場合は、Apache Common Utilsクラスorg.apache.commons.collections.list.SetUniqueListがあり、Listインターフェースと一意の制約を提供します。ただし、これはListインターフェースを壊します。ただし、インデックスでリストをシークする必要がある場合は、これによりパフォーマンスが向上します。Setインターフェースを処理でき、データセットが小さい場合は、LinkedHashSetが適しています。それはソフトウェアの設計と意図に依存します。

繰り返しますが、各コレクションには特定の長所と短所があります。一部の高速挿入で遅い読み取り、一部の高速読み取りで遅い挿入など。コレクションのドキュメントをかなりの時間をかけて各クラスとインターフェイスの詳細を完全に学習することは理にかなっています。


3
これは質問に対する答えを提供しません。批評したり、著者に説明を要求したりするには、投稿の下にコメントを残します。自分の投稿にはいつでもコメントできます。十分な評判得られれ、どの投稿にもコメントできます。
Zach Saucier、2014

1
それは実際に答えを提供します。セットのように機能するリストだけが必要な場合は、org.apache.commons.collections.list.SetUniqueListを使用しますが、プログラマーとしては、それよりも注意深く、問題についてより深く考える必要があります。これが私の答えをより良くするなら、「Javaでユニークなリストを作成する方法?」リストuniqueList = new SetUniqueList();,それが方法です...
Paul Connolly

3
そしてザック、私はジャークになるつもりはありませんが、あなたのコメントの前に私の答えを読みましたか?それともあなたはそれを理解していませんか?理解できなくても大丈夫です-お知らせください。このトピックについて詳しく説明します。誰かの質問に親しみやすい答えを出すために、データ構造に関する論文を書く必要があるとは思いません。また、私が答えを知っていて、他の誰も実際にそれを提供していない場合、私はコメントの評判を確立するための素直な方法については気にしません。
Paul Connolly

1
ちなみに、筆者に批判したり、説明を求めたりすることはありませんでした。単に、A)提供したクラスをすぐに使用できる、またはB)時間をかけてこれらのクラスの違いを本当に理解して関連づけることができると言っていました。彼のニーズにそれらを。Bは明らかに時間がかかりますが、長期的にはより良いコードになります。
Paul Connolly

8

使用new HashSet<String> 例:

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}

2
[アーロン、アリス、ジェームズ、アデル、ホセ、ジェレミー、エイミー、アラン、パトリック、ヘレン、アレクシー]: -ただ、上記のプログラムのプットを追加する> 11個の文字がに送信されなければならない
Ammad

4

あなただけHashSet<String>のユニークなオブジェクトのコレクションを維持するために使用できます。Integerマップの値が重要な場合は、代わりcontainsKeyにマップのメソッドを使用して、キーがすでにマップにあるかどうかをテストできます。


3

HashSet<String>(または)Set実装はあなたのために仕事をするかもしれません。Set重複を許可しないでください。

これがHashSetのjavadocです。


2

これがどれほど効率的かはわかりませんが、単純なコンテキストではうまくいきました。

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }

1

java.util.Set<E>インターフェイスの実装クラスの1つ、たとえばjava.util.HashSet<String> コレクションクラスを使用することもできます。

重複する要素を含まないコレクション。より正式には、セットにはe1.equals(e2)のような要素e1とe2のペアが含まれず、最大で1つのnull要素が含まれます。その名前が示すように、このインターフェースは数学セットの抽象化をモデル化しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.