web-dev-qa-db-ja.com

リストから重複を削除する方法は?

リストから重複を削除したいのですが、私がやっていることは機能していません:

List<Customer> listCustomer = new ArrayList<Customer>();    
for (Customer customer: tmpListCustomer)
{
  if (!listCustomer.contains(customer)) 
  {
    listCustomer.add(customer);
  }
 }
55
Mercer

そのコードが機能しない場合は、Customerクラスにequals(Object)を適切に実装していない可能性があります。

おそらく、顧客を一意に識別するキーがあります(customerIdと呼びます)。例えば.

_class Customer {
    private String customerId;
    ...
_

equals(Object)の適切な定義は次のようになります。

_    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Customer)) {
            return false;
        }
        Customer other = (Customer) obj;
        return this.customerId.equals(other.customerId);
    }
_

完全を期すために、shouldhashCodeを実装して、等しい2つのCustomerオブジェクトが同じハッシュ値を返すようにします。上記のhashCodeの定義に一致するequalsは次のようになります。

_    public int hashCode() {
        return customerId.hashCode();
    }
_

リストが大きい場合、これは重複を削除する効率的な方法ではないことにも注意してください。 (N人の顧客を含むリストの場合、最悪の場合にN*(N-1)/2比較を実行する必要があります。つまり、重複がない場合です。)より効率的なソリューションの場合は、HashSet重複チェックを行います。

48
Stephen C

現在の順序を維持したい場合Setを望まない場合、おそらく最も簡単です:

List<Customer> depdupeCustomers =
    new ArrayList<>(new LinkedHashSet<>(customers));

元のリストを変更する場合:

Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);
89

Java 8アップデート
次のように配列のストリームを使用できます。

Arrays.stream(yourArray).distinct()
                    .collect(Collectors.toList());
25
Alireza Alallah

お客様はequals()契約を実装していますか?

equals()およびhashCode()を実装していない場合、listCustomer.contains(customer)は、まったく同じインスタンスはすでにリストに存在します(インスタンスによって、まったく同じオブジェクトを意味します-メモリアドレスなど)。探しているのが同じかどうかをテストする場合Customer(おそらく同じ顧客名または顧客番号を持っている場合は同じ顧客です)が既にリストにある場合は、equals()をオーバーライドして、関連するフィールド(顧客名など)が一致するかどうかを確認する必要があります。

注:hashCode()をオーバーライドする場合は、equals()をオーバーライドすることを忘れないでください!そうしないと、HashMapおよびその他のデータ構造に問題が生じる可能性があります。これがなぜなのか、どのような落とし穴を避けるのが良いかについては、Josh Blochの Effective Javaequals()およびhashCode()(リンクhashCode()を実装するときにequals()を実装しなければならない理由に関する情報のみが含まれていますが、equals()をオーバーライドする方法についても十分な説明があります。

ところで、セットには順序の制限がありますか?存在しない場合、この問題を解決する少し簡単な方法は、Set<Customer> そのようです:

Set<Customer> noDups = new HashSet<Customer>();
noDups.addAll(tmpListCustomer);
return new ArrayList<Customer>(noDups);

セットでは重複が許可されないため、重複がうまく削除されます。ただし、tmpListCustomerには明示的な順序がないため、HashSetに適用された順序は失われます(TreeSetを使用して回避できますが、正確には関係ありません)あなたの質問に)。これにより、コードを少し簡素化できます。

13
Scott Fines

リスト→セット→リスト(個別)

すべての要素を Set に追加するだけです。要素の繰り返しは許可されません。後でリストが必要な場合は、後で新しいArrayList(theSet)コンストラクターを使用します(theSetは結果セットです)。

13
folone

Customer.equals()が適切に(またはまったく)実装されていない可能性があります。

List.contains()は、equals()を使用して、その要素のいずれかがパラメーターとして渡されたオブジェクトと同一であるかどうかを検証します。ただし、equalsのデフォルトの実装では、値のアイデンティティではなく物理的なアイデンティティをテストします。したがって、Customerで上書きしていない場合、同じ状態の2つの異なるCustomerオブジェクトに対してfalseを返します。

equalsの実装方法 (およびそのペアである hashCode の基本的な詳細は次のとおりです-実際には常に実装する必要がありますどちらかを実装する必要がある場合は両方)。 Customerクラスを表示していないため、より具体的なアドバイスを提供することは困難です。

他の人が指摘したように、手作業で作業するよりもSetを使用した方が良いですが、その場合でも、これらのメソッドを実装する必要があります。

9
Péter Török

「contains」メソッドは、Customer.equals(Object o)からtrueを返すエントリがリストに含まれているかどうかを検索しました。 Customerまたはその親のいずれかでequals(Object)をオーバーライドしていない場合、同じオブジェクトの既存のオカレンスのみを検索します。これはあなたが望んでいたものかもしれません。その場合、コードは動作するはずです。ただし、同じ顧客を表す2つのオブジェクトがないことを探している場合は、equals(Object)をオーバーライドしてtrueを返す必要があります。

また、Listの代わりにSetの実装の1つを使用すると、重複した削除が自動的に、より高速になります(非常に小さなリスト以外の場合)。同等のコードを提供する必要があります。

Equals()をオーバーライドする場合は、hashCode()もオーバーライドする必要があります。

5
DJClayworth
private void removeTheDuplicates(List<Customer>myList) {
    for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
        Customer customer = iterator.next();
        if(Collections.frequency(myList, customer) > 1) {
            iterator.remove();
        }
    }
    System.out.println(myList.toString());

}
5
Bade

上記の答えはほぼすべて正しいですが、パフォーマンスを上げるためではなく、関連リストの作成中にマップまたはセットを使用することをお勧めします。リストをセットまたはマップに変換してからリストに再変換するのは簡単な作業だからです。

サンプルコード:

Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set 
//prevents the adding order of the elements
for (String string: stringsList) {
    stringsSet.add(string);
}
return new ArrayList<String>(stringsSet);
3

2つの提案:

  • ArrayListの代わりにHashSetを使用します。これにより、長いリストがある場合は、contains()チェックが大幅に高速化されます。

  • Customer.equals()およびCustomer.hashCode()が適切に実装されていることを確認します。つまり、これらは顧客オブジェクトの基になるフィールドの結合値に基づいている必要があります。

3
mikera

私見の最近のやり方:

コレクション "dups"があり、同じ要素を含むがすべての重複を排除した別のコレクションを作成するとします。次のワンライナーがトリックを行います。

Collection<collectionType> noDups = new HashSet<collectionType>(dups);

定義上、重複を含むことができないSetを作成することで機能します。

Oracle文書に基づいています

1
Daniel Perník

最もクリーンな方法は次のとおりです。

List<XXX> lstConsultada = dao.findByPropertyList(YYY);
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));

各エンティティのIdのプロパティでhascodeおよびequalsをオーバーライドします

1
Eduardo

他の人が述べたように、おそらくequals()を正しく実装していないでしょう。

ただし、ランタイムは2乗した要素の数になる可能性があるため、このコードは非常に非効率的であると見なされることにも注意してください。

代わりにリストではなくセット構造を使用するか、最初にセットを構築してからリストに変換することを検討してください。

1
Uri

Javaの正しい答えは、 Set を使用することです。すでに_List<Customer>_があり、それを複製したい場合

_Set<Customer> s = new HashSet<Customer>(listCustomer);
_

それ以外の場合は、Set実装HashSetTreeSetを直接使用し、List構築フェーズをスキップします。

Setに置かれているドメインクラスで hashCode()およびequals() もオーバーライドする必要があります。あなたが実際にあなたが得るものが欲しい。 equals()は、オブジェクトの一意のIDを比較するだけの単純なものから、すべてのフィールドを比較するのと同じくらい複雑にすることができます。 hashCode()は、一意のid 'String表現のhashCode()またはhashCode()を返すのと同じくらい簡単です。

0
user177800

Java 8ストリームAPIを使用します。

    List<String> list = new ArrayList<>();
    list.add("one");
    list.add("one");
    list.add("two");
    System.out.println(list);
    Collection<String> c = list.stream().collect(Collectors.toSet());
    System.out.println(c);

出力:

値の前:[1、1、2]

値の後:[1、2]

0
Kannan Msk