Java:ArrayListで重複を検出しますか?


104

ArrayListにJavaの同じ要素が複数含まれているかどうかを検出(true / falseを返す)するにはどうすればよいですか?

ありがとう、テリー

編集 「ブロック」を互いに比較するのではなく、それらの整数値を比較しているわけではないことを忘れてしまいました。各「ブロック」にはintがあり、これがそれらの違いです。「getNum」という名前のメソッド(例:table1 [0] [2] .getNum();)を呼び出して、特定のブロックのintを見つけます。


"Block"がintによって比較される場合、おそらくhashCodeが同じintを返し、equalsがそれらのintを比較する必要があります。
ポールトンブリン

リストの代わりにセットを使用
dmarquina

回答:


192

最も簡単な方法:(Set(Collection)コンストラクターまたはSet.addAllを使用して)コレクション全体をセットにダンプし、セットがArrayListと同じサイズかどうかを確認します。

List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

更新:私があなたの質問を正しく理解している場合、次のようにブロックの2D配列があります

ブロックテーブル[] [];

それらの行に重複があるかどうかを検出したいですか?

その場合、Blockが「equals」と「hashCode」を正しく実装していると想定して、次のことを実行できます。

for (Block[] row : table) {
   Set set = new HashSet<Block>(); 
   for (Block cell : row) {
      set.add(cell);
   }
   if (set.size() < 6) { //has duplicate
   }
}

構文については100%確実ではないので、次のように書く方が安全かもしれません。

for (int i = 0; i < 6; i++) {
   Set set = new HashSet<Block>(); 
   for (int j = 0; j < 6; j++)
    set.add(table[i][j]);
 ...

Set.add追加される項目がすでにセットにある場合はブール値のfalseを返します。したがってfalse、重複があるかどうかを知りたいだけの場合に返されるaddを短絡してベールアウトすることもできます。


13
hashCode / equalsも実装してください。
jon077

1
または少し簡単です:addAllを使用する代わりに、セットを作成するときにラップします(例:new HashSet(list))。
Fabian Steeg、

2
@ jon077:それは「重複」の定義に依存します。
マイケル・マイヤーズ

2D配列の要素を検出するプロセスは同じですか?たとえば、array [0] [0]からarray [0] [6](「行」)までチェックします。どうもありがとう、テリー

配列内の各オブジェクトは整数値を保持します。「複製」することにより、オブジェクトは同じ整数値を持ちます。

60

Set#addリストとセットのサイズを比較する代わりにの戻り値を使用してコードを改善

public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

7
割り当てるスペースの量をHashSetに伝える方が効率的Set<T> set = new HashSet<T>(list.size());でしょうか?リストパラメータを指定すると、リストに重複が含まれないことが一般的である場合、より効率的だと思います。
ポールジャクソン

1
完全なリストに基づく@PaulJacksonのサイジングは、おそらく有益でしょう。ただし、一般的なケースが重複を早期に見つけることである場合、スペースが無駄になりました。またHashSet、リストのサイズに合わせてをサイジングしても、ハッシュ構造の基本的な負荷係数のため、リスト全体を実行するとサイズが変更されます。
ジェイアンダーソン

1
ランタイムやスペースに関する実際の問題が発生しない限り、そのようにコードを微調整することはありません。時期尚早の最適化を回避するのが最善です。
akuhn 2018年

15

重複をまったく避けたい場合は、重複を検出する中間プロセスを省略して、Setを使用する必要があります。


1
必ずhashCode / equalsを実装してください:)
jon077

@ jon077:今言ったように、必ずしもそうとは限りません。
マイケルマイヤーズ

1
ただし、セットを使用しても重複は検出されません。それらを防ぐだけです。もちろん、上記の@akuhnで指摘されているように、addメソッドの結果を確認しない限り。
mcallahan 2017年

13

重複する要素を返すようにコードを改善

  • コレクション内の重複を見つけることができます
  • 重複のセットを返す
  • セットから固有の要素を取得できます

public static <T> List getDuplicate(Collection<T> list) {

    final List<T> duplicatedObjects = new ArrayList<T>();
    Set<T> set = new HashSet<T>() {
    @Override
    public boolean add(T e) {
        if (contains(e)) {
            duplicatedObjects.add(e);
        }
        return super.add(e);
    }
    };
   for (T t : list) {
        set.add(t);
    }
    return duplicatedObjects;
}


public static <T> boolean hasDuplicate(Collection<T> list) {
    if (getDuplicate(list).isEmpty())
        return false;
    return true;
}

それはかなり素晴らしいです。無効なコードがあり、おそらくそれが最適な方法ではないかもしれませんが、あなたのアプローチは完全に揺るぎません!(そしてそれは素晴らしい働きをします)
Jules Colle

9

要素が何らかの形で比較可能である場合(順序に実際の意味があるという事実は無関心です-同等性の定義と一致している必要があるだけです)、最速の重複除去ソリューションはリストをソートします(0(n log( n)))次に、単一のパスを実行し、繰り返されるものを探します要素(つまり、互いに続く等しい要素)を(これはO(n)です)。

全体的な複雑さはO(n log(n))になります。これは、Set(n倍の長さ(n))で得られるものとほぼ同じですが、定数ははるかに小さくなります。これは、並べ替え/重複除去の定数が要素の比較のコストから生じるのに対し、セットからのコストは、ハッシュ計算と1つ(場合によっては複数)のハッシュ比較から生じる可能性が高いためです。ハッシュベースのSet実装を使用している場合、つまり、ツリーベースではO(nlog²(n))が得られるため、さらに悪い結果になります。

ただし、私が理解しているように、重複を削除する必要はありませんが、重複をテストするだけです。だから、あなたの配列にマージまたはヒープソートアルゴリズムを手動でコーディングする必要があります。それは、コンパレータが0を返した場合にtrue(つまり「dupがある」)を返すだけで終了し、そうでない場合はソートを完了し、ソートされた配列を走査して繰り返しをテストします。 。実際、マージソートまたはヒープソートでは、ソートが完了すると、両方の要素がすでに最終位置にある場合を除いて、すべての重複ペアが比較されます(これはありそうもありません)。したがって、微調整されたソートアルゴリズムにより、パフォーマンスが大幅に向上するはずです(私はそれを証明する必要がありますが、微調整されたアルゴリズムは、一様にランダムなデータのO(log(n))にあるはずです)。


この場合、nは6なので、実装の詳細に多くの時間を費やすことはありませんが、そのようなことをする必要がある場合は、特別なヒープソートの考えを維持します。
ポールトンブリン

3番目の段落がわかりません。マージソートとヒープソートは、どちらもO(nlog(n))であり、O(log(n))ではありません。重複を特定して終了しても、時間の複雑さは変わりません...
ChaimKut

8

に対して同様の操作を行う必要がありましたStreamが、良い例が見つかりませんでした。これが私が思いついたものです。

public static <T> boolean areUnique(final Stream<T> stream) {
    final Set<T> seen = new HashSet<>();
    return stream.allMatch(seen::add);
}

これには、ストリーム全体を処理する必要がなく、重複が早期に見つかった場合に短絡するという利点があり、すべてをaに入れSetてサイズを確認するよりもはるかに複雑ではありません。したがって、このケースはおおよそ次のようになります。

List<T> list = ...
boolean allDistinct = areUnique(list.stream());

7

Java 8以降では、Stream APIを使用できます。

boolean areAllDistinct(List<Block> blocksList) {
    return blocksList.stream().map(Block::getNum).distinct().count() == blockList.size();
}

2

簡単に言えば、1)すべてのアイテムが比較可能であることを確認します。2)配列をソートします。2)配列を反復処理して、重複を見つけます


1

リスト内の重複を知るには、次のコードを使用します。重複を含むセットが表示されます。

 public Set<?> findDuplicatesInList(List<?> beanList) {
    System.out.println("findDuplicatesInList::"+beanList);
    Set<Object> duplicateRowSet=null;
    duplicateRowSet=new LinkedHashSet<Object>();
            for(int i=0;i<beanList.size();i++){
                Object superString=beanList.get(i);
                System.out.println("findDuplicatesInList::superString::"+superString);
                for(int j=0;j<beanList.size();j++){
                    if(i!=j){
                         Object subString=beanList.get(j);
                         System.out.println("findDuplicatesInList::subString::"+subString);
                         if(superString.equals(subString)){
                             duplicateRowSet.add(beanList.get(j));
                         }
                    }
                }
            }
            System.out.println("findDuplicatesInList::duplicationSet::"+duplicateRowSet);
        return duplicateRowSet;
  }

1

この問題を処理する最良の方法は、HashSetを使用することです。

ArrayList<String> listGroupCode = new ArrayList<>();
listGroupCode.add("A");
listGroupCode.add("A");
listGroupCode.add("B");
listGroupCode.add("C");
HashSet<String> set = new HashSet<>(listGroupCode);
ArrayList<String> result = new ArrayList<>(set);

結果の配列リストを印刷して 、重複なしで結果を確認するだけです:)


1

重複する値のセットが必要な場合:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindDuplicateInArrayList {

    public static void main(String[] args) {

        Set<String> uniqueSet = new HashSet<String>();
        List<String> dupesList = new ArrayList<String>();
        for (String a : args) {
            if (uniqueSet.contains(a))
                dupesList.add(a);
            else
                uniqueSet.add(a);
        }
        System.out.println(uniqueSet.size() + " distinct words: " + uniqueSet);
        System.out.println(dupesList.size() + " dupesList words: " + dupesList);
    }
}

また、場合によっては、値のトリミングや小文字の使用も検討してください。


重複が必要な場合の最も簡単で最良の答えは、パフォーマンスのために、argsのサイズでuniqueSetヒントを初期化することです。
Christophe Roussy

0
    String tempVal = null;
    for (int i = 0; i < l.size(); i++) {
        tempVal = l.get(i); //take the ith object out of list
        while (l.contains(tempVal)) {
            l.remove(tempVal); //remove all matching entries
        }
        l.add(tempVal); //at last add one entry
    }

注:リストの先頭からアイテムが削除されるため、これによりパフォーマンスが大幅に低下します。これに対処するには、2つのオプションがあります。1)逆の順序で繰り返し、要素を削除します。2)ArrayListの代わりにLinkedListを使用します。他のコレクションを使用せずにリストから重複を削除するようにインタビューで偏った質問のため、上記の例が答えです。ただし、現実の世界では、これを達成する必要がある場合は、要素をリストからセットに配置するだけです。


0
/**
     * Method to detect presence of duplicates in a generic list. 
     * Depends on the equals method of the concrete type. make sure to override it as required.
     */
    public static <T> boolean hasDuplicates(List<T> list){
        int count = list.size();
        T t1,t2;

        for(int i=0;i<count;i++){
            t1 = list.get(i);
            for(int j=i+1;j<count;j++){
                t2 = list.get(j);
                if(t2.equals(t1)){
                    return true;
                }
            }
        }
        return false;
    }

オーバーライドした具象クラスの例equals()

public class Reminder{
    private long id;
    private int hour;
    private int minute;

    public Reminder(long id, int hour, int minute){
        this.id = id;
        this.hour = hour;
        this.minute = minute;
    }

    @Override
    public boolean equals(Object other){
        if(other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        Reminder otherReminder = (Reminder) other;
        if(this.hour != otherReminder.hour) return false;
        if(this.minute != otherReminder.minute) return false;

        return true;
    }
}

0
    ArrayList<String> withDuplicates = new ArrayList<>();
    withDuplicates.add("1");
    withDuplicates.add("2");
    withDuplicates.add("1");
    withDuplicates.add("3");
    HashSet<String> set = new HashSet<>(withDuplicates);
    ArrayList<String> withoutDupicates = new ArrayList<>(set);

    ArrayList<String> duplicates = new ArrayList<String>();

    Iterator<String> dupIter = withDuplicates.iterator();
    while(dupIter.hasNext())
    {
    String dupWord = dupIter.next();
    if(withDuplicates.contains(dupWord))
    {
        duplicates.add(dupWord);
    }else{
        withoutDupicates.add(dupWord);
    }
    }
  System.out.println(duplicates);
  System.out.println(withoutDupicates);

現在の問題を修正する方法で、この答えのヘルプOPのための答えを持ついくつかの説明を追加
ρяσѕρєяK

0

この回答はKotlinで書かれていますが、Javaに簡単に翻訳できます。

arraylistのサイズが一定の小さな範囲内にある場合、これは優れたソリューションです。

var duplicateDetected = false
    if(arrList.size > 1){
        for(i in 0 until arrList.size){
            for(j in 0 until arrList.size){
                if(i != j && arrList.get(i) == arrList.get(j)){
                    duplicateDetected = true
                }
            }
        }
    }

0
private boolean isDuplicate() {
    for (int i = 0; i < arrayList.size(); i++) {
        for (int j = i + 1; j < arrayList.size(); j++) {
            if (arrayList.get(i).getName().trim().equalsIgnoreCase(arrayList.get(j).getName().trim())) {
                return true;
            }
        }
    }

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