2つのArrayListの差を計算するにはどうすればよいですか?


81

2つのArrayListがあります。

ArrayListAには次のものが含まれます。

['2009-05-18','2009-05-19','2009-05-21']

ArrayListBには次のものが含まれます。

['2009-05-18','2009-05-18','2009-05-19','2009-05-19','2009-05-20','2009-05-21','2009-05-21','2009-05-22']

ArrayListAとArrayListBを比較する必要があります。結果のArrayListには、ArrayListAに存在しないリストが含まれている必要があります。

ArrayListの結果は次のようになります。

['2009-05-20','2009-05-22']

比較する方法は?

回答:


194

Javaでは、CollectionインターフェースのremoveAllメソッドを使用できます。

// Create a couple ArrayList objects and populate them
// with some delicious fruits.
Collection firstList = new ArrayList() {{
    add("apple");
    add("orange");
}};

Collection secondList = new ArrayList() {{
    add("apple");
    add("orange");
    add("banana");
    add("strawberry");
}};

// Show the "before" lists
System.out.println("First List: " + firstList);
System.out.println("Second List: " + secondList);

// Remove all elements in firstList from secondList
secondList.removeAll(firstList);

// Show the "after" list
System.out.println("Result: " + secondList);

上記のコードは、次の出力を生成します。

First List: [apple, orange]
Second List: [apple, orange, banana, strawberry]
Result: [banana, strawberry]

7
リストがカスタムクラスの場合、クラスのequalsメソッドをオーバーライドする必要がありますよね?
RTF

5
@RTFはい、equalsオブジェクトを比較できるようにする実装を提供する必要があります。実装hashCodeについてもお読みください。例えば、どのようにノートString::equals大文字と小文字を区別は、その「りんご」と「Appleは」同じとみなされることはありません。
バジルバーク2016年

1
実際、答えはあなたが何をしたいかによります。RemoveAllは重複を保持しません。2番目のリストに別の「apple」文字列を追加すると、その文字列も削除されますが、必ずしも必要なものとは限りません。
ジュールテスタード2016年

2
これはとても非効率的です。これが選択された回答であり、最も評価の高い回答であるのは悲しいことです。removeAllfirstList.containsすべての要素を呼び出しますsecondList。を使用するHashSetとそれを防ぐことができ、より低いいくつかの良い答えがあります。
vlasec 2018

20

あなたはすでに正しい答えを持っています。また、リスト(コレクション)間でより複雑で興味深い操作を行いたい場合は、apache commonsコレクションCollectionUtils)を使用します。これにより、結合/論理和を作成し、共通部分を見つけ、1つのコレクションが別のコレクションのサブセットであるかどうかを確認できます。



12

ストリームを使用するJava8では、実際には非常に簡単です。編集:ストリームなしで効率的にすることができます、以下を参照してください。

List<String> listA = Arrays.asList("2009-05-18","2009-05-19","2009-05-21");
List<String> listB = Arrays.asList("2009-05-18","2009-05-18","2009-05-19","2009-05-19",
                                   "2009-05-20","2009-05-21","2009-05-21","2009-05-22");

List<String> result = listB.stream()
                           .filter(not(new HashSet<>(listA)::contains))
                           .collect(Collectors.toList());

ハッシュセットは一度だけ作成されることに注意してください。メソッド参照はそのcontainsメソッドに関連付けられています。ラムダで同じことを行うには、変数にセットを含める必要があります。変数を作成することは悪い考えではありません。特に、見苦しい、または理解しにくい場合はそうです。

否定メソッド参照を直接呼び出すことはできないため、このユーティリティメソッド(または明示的なキャスト)のようなものがないと、述語を簡単に否定することはできません(最初に型推論が必要です)。

private static <T> Predicate<T> not(Predicate<T> predicate) {
    return predicate.negate();
}

ストリームにfilterOutメソッドなどがあると、見栄えが良くなります。


また、@ Holgerは私にアイデアをくれました。複数の削除に最適化されArrayListremoveAllメソッドがあり、要素を1回だけ再配置します。ただし、contains指定されたコレクションによって提供される方法を使用するためlistA、小さい場合はその部分を最適化する必要があります。

listAし、listB以前に宣言され、このソリューションは、Java 8を必要とせず、それは非常に効率的です。

List<String> result = new ArrayList(listB);
result.removeAll(new HashSet<>(listA));

1
@Baxなぜ編集するのですか?オリジナルはよりクリーンで機能的に同一でした。
shmosel 2018年

1
@Baxいいえ、そうではありません。
shmosel 2018年

1
Guavaを使用すると、次のことができますPredicates.in(new HashSet<>(listA)).negate()
shmosel

1
いくつかのテストを実行したところ、このソリューションはlistB.removeAll(new HashSet <>(listA))よりも約10〜20%高速です。およびGuavaSets.difference(...)siはストリームより2倍遅い。
テレボグ

1
@VlasecArrayList.removeは線形の複雑さを持っArrayList.removeAllremoveいますが、線形配列の更新操作に依存せずに実行し、残りの各要素を最終的な場所にコピーします。対照的に、のリファレンス実装にLinkedListは最適化されていませんremoveAllが、remove影響を受ける要素ごとに操作が実行され、毎回最大5つのリファレンスが更新されます。だから、除去し、残りの要素との間の比率に応じて、ArrayListのはremoveAllまだ良いよりも有意に行うことLinkedListも、巨大なリストのために、S '。
ホルガー

9

編集:元の質問は言語を指定していませんでした。私の答えはC#です。

代わりに、この目的のためにHashSetを使用する必要があります。ArrayListを使用する必要がある場合は、次の拡張メソッドを使用できます。

var a = arrayListA.Cast<DateTime>();
var b = arrayListB.Cast<DateTime>();    
var c = b.Except(a);

var arrayListC = new ArrayList(c.ToArray());

HashSetの使用...

var a = new HashSet<DateTime>(); // ...and fill it
var b = new HashSet<DateTime>(); // ...and fill it
b.ExceptWith(a); // removes from b items that are in a

8

GuavaSets.differenceを使用しました

パラメータはセットであり、一般的なコレクションではありませんが、任意のコレクション(一意のアイテムを含む)からセットを作成する便利な方法は、Guava ImmutableSet.copyOf(Iterable)です。

(私は最初に関連/重複の質問にこれ投稿しましたが、これまで欠けていた良いオプションだと思うので、ここにもコピーしています。)


8

これはJava8の非常に古い質問ですが、次のようなことができます。

 List<String> a1 = Arrays.asList("2009-05-18", "2009-05-19", "2009-05-21");
 List<String> a2 = Arrays.asList("2009-05-18", "2009-05-18", "2009-05-19", "2009-05-19", "2009-05-20", "2009-05-21","2009-05-21", "2009-05-22");

 List<String> result = a2.stream().filter(elem -> !a1.contains(elem)).collect(Collectors.toList());

私はJava8が大好きですが、それでも複雑さについて考える必要があります。リストにもありますがCollection'sメソッドがcontainsありますが、非常に非効率的です。見つからない場合は、リスト全体を通過する必要があります。のすべての要素に対してそれを行うと、a2大きなリストでは痛々しいほど遅くなる可能性があります。そのため、a1私は自分の答えからセットを作成します。
vlasec 2018年

2

あなたはC#について話していると思います。もしそうなら、あなたはこれを試すことができます

    ArrayList CompareArrayList(ArrayList a, ArrayList b)
    {
        ArrayList output = new ArrayList();
        for (int i = 0; i < a.Count; i++)
        {
            string str = (string)a[i];
            if (!b.Contains(str))
            {
                if(!output.Contains(str)) // check for dupes
                    output.Add(str);
            }
        }
        return output;
    }

申し訳ありませんが、プログラミング言語については触れませんでした。大丈夫ですが、再生にJavaを使用する必要があります
naveen 2009年

これは正しいです。しかし、それは非常に非効率的な方法でもあります。基本的に、bリストa.Count時間全体を循環します。HashSet代わりにを作成して、ContainsまたはRemoveAllセットのメソッドを使用して、必要な結果を正確に取得できます。
vlasec 2018

1

文字列を比較しているだけです。

ArrayListAの値をHashTableAのキーとして配置します。
の値をHashTableBのキーとして配置します。

次に、HashTable Aの各キーについて、存在する場合はHashTableBから削除します。

HashTable Bに残っているのは、ArrayList Aの値ではなかった文字列(キー)です。

コードの要求に応じて追加されたC#(3.0)の例:

List<string> listA = new List<string>{"2009-05-18","2009-05-19","2009-05-21'"};
List<string> listB = new List<string>{"2009-05-18","2009-05-18","2009-05-19","2009-05-19","2009-05-20","2009-05-21","2009-05-21","2009-05-22"};

HashSet<string> hashA = new HashSet<string>();
HashSet<string> hashB = new HashSet<string>();

foreach (string dateStrA in listA) hashA.Add(dateStrA);
foreach (string dateStrB in listB) hashB.Add(dateStrB);

foreach (string dateStrA in hashA)
{
    if (hashB.Contains(dateStrA)) hashB.Remove(dateStrA);
}

List<string> result = hashB.ToList<string>();

C#コードでは、hashA変数は事実上役に立たない。繰り返さlistAれるhashAだけでContains呼び出されることはないので、代わりにforeachを作成できます。
vlasec 2018

(また、C#にJavaのようにRemoveAllメソッドがある場合は、独自のサイクルを作成することを回避できます...しかし、このソリューションは少なくとも選択したソリューションよりもはるかに効率的であるため、私はあなたに賛成しました。)
Vlasec

1

こんにちはこのクラスを使用すると、両方のリストが比較され、両方のリストの不一致が正確に表示されます。

import java.util.ArrayList;
import java.util.List;


public class ListCompare {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> dbVinList;
        dbVinList = new ArrayList<String>();
        List<String> ediVinList;
        ediVinList = new ArrayList<String>();           

        dbVinList.add("A");
        dbVinList.add("B");
        dbVinList.add("C");
        dbVinList.add("D");

        ediVinList.add("A");
        ediVinList.add("C");
        ediVinList.add("E");
        ediVinList.add("F");
        /*ediVinList.add("G");
        ediVinList.add("H");
        ediVinList.add("I");
        ediVinList.add("J");*/  

        List<String> dbVinListClone = dbVinList;
        List<String> ediVinListClone = ediVinList;

        boolean flag;
        String mismatchVins = null;
        if(dbVinListClone.containsAll(ediVinListClone)){
            flag = dbVinListClone.removeAll(ediVinListClone);   
            if(flag){
                mismatchVins = getMismatchVins(dbVinListClone);
            }
        }else{
            flag = ediVinListClone.removeAll(dbVinListClone);
            if(flag){
                mismatchVins = getMismatchVins(ediVinListClone);
            }
        }
        if(mismatchVins != null){
            System.out.println("mismatch vins : "+mismatchVins);
        }       

    }

    private static String getMismatchVins(List<String> mismatchList){
        StringBuilder mismatchVins = new StringBuilder();
        int i = 0;
        for(String mismatch : mismatchList){
            i++;
            if(i < mismatchList.size() && i!=5){
                mismatchVins.append(mismatch).append(",");  
            }else{
                mismatchVins.append(mismatch);
            }
            if(i==5){               
                break;
            }
        }
        String mismatch1;
        if(mismatchVins.length() > 100){
            mismatch1 = mismatchVins.substring(0, 99);
        }else{
            mismatch1 = mismatchVins.toString();
        }       
        return mismatch1;
    }

}

クローンは実際にはまったくクローンではないことをご存知ですか?
vlasec 2018

1

これはArraylistでも機能します

    // Create a couple ArrayList objects and populate them
    // with some delicious fruits.
    ArrayList<String> firstList = new ArrayList<String>() {/**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("pea");
    }};

    ArrayList<String> secondList = new ArrayList<String>() {

    /**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("banana");
        add("strawberry");
    }};

    // Show the "before" lists
    System.out.println("First List: " + firstList);
    System.out.println("Second List: " + secondList);

    // Remove all elements in firstList from secondList
    secondList.removeAll(firstList);

    // Show the "after" list
    System.out.println("Result: " + secondList);

1
出力:最初のリスト:[リンゴ、オレンジ、ピッポ] 2番目のリスト:[リンゴ、オレンジ、バナナ、イチゴ]結果:[バナナ、イチゴ]
サイコ

します。しかし、そう言うときは、大きなリストでは非常に遅くなる可能性があることに注意することを忘れないでください。念頭に置いてクマのようなメソッドことremovecontainsリスト全体を検索する必要があります。サイクルで繰り返し呼び出されると(で発生しますremoveAll)、2次の複雑さが得られます。ただし、ハッシュセットを使用して、線形にすることもできます。
vlasec 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.