web-dev-qa-db-ja.com

2つのリストを比較して違いを取得する

2つのリストがあります。それらには異なるタイプのオブジェクトが含まれていますが、両方のタイプにidと名前が含まれており、idは私が比較しているものです。リスト1はDBから取得され、リスト2はフロントエンドから送信されます。

私がする必要があるのは、それらをループして、どのリスト項目が新しく追加され、どれが削除されたかを見つけることです。

できましたが、問題は見苦しいことです。

Idとnameを持つことができるNameDTOと呼ばれるオブジェクトがあるとしましょう。リスト2は、そのタイプのオブジェクトで満たされています。

これは私がやった方法です:

final ArrayList<NamedDTO> added = new ArrayList<>();
final ArrayList<NamedDTO> removed = new ArrayList<>();

for(NamedDTO listTwoObject : listTwo) {
   boolean contained = false;
   for(SomeObject listOneObject : listOne) {
       if(listTwoObject.getId().equals(listOneObject.getId()) {
           contained = true;
       }
   }
   if(!contained) {
      added.add(listTwoObject);
   }
}

for(SomeObject listOneObject : listOne) {
   boolean contained = false;
   for(NamedDTO listTwoObject : listTwo) {
       if(listTwoObject.getId().equals(listOneObject.getId()) {
           contained = true;
       }
   }
   if(!contained) {
      removed.add(new NamedDTO(listOneObject.getId(), listOneObject.getName()));
  }
}

これは動作します、私はそれをテストしました。より良い解決策はありますか?セットを比較して比較できるように、セットを使用することを考えていましたが、それには欠点がありますか?

12
mirzak

私が正しく理解している場合、これはシナリオ例です:

  • listOne [datab] items:_[A, B, C, D]_
  • listTwo [front]アイテム:_[B, C, D, E, F]_

効果として取得する必要があるものは次のとおりです。

  • 追加:_[E, F]_
  • 削除済み:_[A]_

まず最初に、タイプアダプターを使用するか、1つの共通クラスとoverrideの異なるタイプをequalsメソッドに拡張して、idおよびnameで一致できるようにします

第二に、これはセットでの非常に簡単な操作です(セットを使用できますが、リストも問題ありません)。ライブラリの使用をお勧めします: https://commons.Apache.org/proper/commons-collections/apidocs/org/Apache/commons/collections4/CollectionUtils.html

そして今、基本的に:

  • 追加されるのは_listTwo - listOne_
  • 削除済みは_listOne - listTwo_

およびJavaコードを使用:

  • 追加:CollectionUtils.removeAll(listTwo, listOne)
  • 削除:CollectionUtils.removeAll(listOne, listTwo)

それ以外の場合は、CollectionJava Docs )を実装するすべてのコレクションにもremoveAllメソッドがあり、使用できます。

12
Atais

Java 8ストリームを使用してソリューションを提案します。

    ArrayList<ObjOne> list = new ArrayList<>(Arrays.asList(new ObjOne("1","1"),new ObjOne("3","3"),new ObjOne("2","2")));
    ArrayList<ObjTwo> list2 = new ArrayList<>(Arrays.asList(new ObjTwo("1","1"),new ObjTwo("3","3"),new ObjTwo("4","4")));

    List<ObjOne> removed = list.stream().filter(o1 -> list2.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
            .collect(Collectors.toList());
    System.out.print("added ");
    removed.forEach(System.out::println);

    List<ObjTwo> added = list2.stream().filter(o1 -> list.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
             .collect(Collectors.toList());

    System.out.print("removed ");
    added.forEach(System.out::println);

これは基本的にあなたのソリューションですが、ストリームを使用して実装されているため、コードが短くなり読みやすくなります

10

このネストされたリストの処理は見苦しいだけでなく、非効率的です。 1つのリストのIDをSetに保存して、効率的なルックアップを可能にしてから、Setを使用して他のリストを処理することをお勧めします。この方法では、list1.size()list2.size()操作を実行するのではなく、list1.size()プラスlist2.size()操作を実行します。リスト。次に、両方の操作は基本的に同じであるため、メソッドに抽象化する価値があります。

public static <A,B,R,ID> List<R> extract(
    List<A> l1, List<B> l2, Function<A,ID> aID, Function<B,ID> bID, Function<A,R> r) {

    Set<ID> b=l2.stream().map(bID).collect(Collectors.toSet());
    return l1.stream().filter(a -> !b.contains(aID.apply(a)))
             .map(r).collect(Collectors.toList());
}

このメソッドは次のように使用できます

List<NamedDTO> added   = extract(listTwo, listOne, NamedDTO::getId, SomeObject::getId,
                                 Function.identity());
List<NamedDTO> removed = extract(listOne, listTwo, SomeObject::getId, NamedDTO::getId,
                                 so -> new NamedDTO(so.getId(), so.getName()));

2つのリストを交換するには、ヘルパーメソッドが要素タイプから独立している必要があるため、メソッド参照を介して指定できるidプロパティにアクセスするための関数が必要です。次に、結果要素を記述する関数が必要です。これは、ある場合には恒等関数(NamedDTOを取得する)と、NamedDTOからSomeObjectを構築するラムダ式ですもう一方。

操作自体は上記のように簡単で、1つのリストを反復処理し、idにマップしてSetに収集し、次に他のリストを反復処理して、idがセットにない要素のみを保持します。 、結果タイプにマップし、Listに収集します。

6
Holger

これらのIDが一意であれば、それらをHashSetに入れて、興味のあるIDを見つけることができます。

    Set<Integer> uiList = Stream.of(new FromUI(1, "db-one"), new FromUI(2, "db-two"), new FromUI(3, "db-three"))
            .map(FromUI::getId)
            .collect(Collectors.toCollection(HashSet::new));
    Set<Integer> dbList = Stream.of(new FromDB(3, "ui-one"), new FromDB(5, "ui-five"))
            .map(FromDB::getId)
            .collect(Collectors.toCollection(HashSet::new));

    uiList.removeIf(dbList::remove);

added/uiSet :   [1,2]
removed/dbSet : [5]

入力としてIDと名前を受け取るコンストラクターを使用して、FromUIクラスとFromDBクラスを作成しました。

また、要素がuiSetに含まれているが、dbSetに含まれていない場合は追加されていると仮定します。

2
Eugene