41

同じオブジェクトの 2 つのコレクションがCollection<Foo> oldSetありCollection<Foo> newSetます。必要なロジックは次のとおりです。

  • foo(*) にoldSetあるがそうでない場合はnewSet、コールdoRemove(foo)
  • それ以外の場合fooは inではoldSetなく in の場合newSet、呼び出しますdoAdd(foo)
  • そうでなければfoo、両方のコレクションにあるが変更されている場合は、呼び出しますdoUpdate(oldFoo, newFoo)
  • それ以外の場合!foo.activated && foo.startDate >= nowは、呼び出しますdoStart(foo)
  • それ以外の場合foo.activated && foo.endDate <= nowは、呼び出しますdoEnd(foo)

(*) "in" は一意の識別子が一致することを意味し、必ずしもコンテンツが一致するとは限りません。

removeSet現在の (レガシー) コードは、addSet、 、updateSetstartSetおよびを把握するために多くの比較をendSet行い、ループして各項目に作用します。

コードは非常に厄介です (スパゲッティ ロジックをいくつか既に省略しているためです)。これをリファクタリングしようとしています。その他の背景情報:

  • 私の知る限り、実際にoldSetnewSetArrayList
  • 各セットに含まれるアイテムは 100 未満で、最大で 20 になる可能性が高い
  • このコードは頻繁に呼び出されます (数百万/日で測定されます)、セットはめったに異なりません

私の質問:

  • oldSetID をキーとしてandnewSetを(ここでは順序は関係ありません)に変換するとHashMap<Foo>、コードが読みやすくなり、比較しやすくなりますか? 変換時に失われる時間とメモリのパフォーマンスはどれくらいですか?
  • 2 つのセットを反復して適切な操作を実行すると、より効率的かつ簡潔になりますか?
4

8 に答える 8

36

Apache の commons.collections ライブラリには CollectionUtils クラスがあり、交差、差分、結合など、コレクションの操作/チェックのための使いやすいメソッドを提供します。

org.apache.commons.collections.CollectionUtils API ドキュメントはこちらです。

于 2009-07-22T18:29:03.213 に答える
22

たとえば、Java 8ストリームを使用できます

set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toSet());

または Guavaのクラスを設定します:

Set<String> intersection = Sets.intersection(set1, set2);
Set<String> difference = Sets.difference(set1, set2);
Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2);
Set<String> union = Sets.union(set1, set2);
于 2011-12-26T18:21:34.170 に答える
11

Java で Collections Framework を使用するだけで、あなたが探していると思われるものの概算を作成しました。率直に言って、@Mike Deckが指摘しているように、おそらくやり過ぎだと思います。比較して処理するアイテムのこのような小さなセットの場合、手続き上の観点からは配列の方が適していると思いますが、これが私の疑似コード化された (私は怠け者なので) ソリューションです。Foo クラスは、コンテンツ内のすべてのデータではなく、一意の ID に基づいて比較できると仮定しています。

Collection<Foo> oldSet = ...;
Collection<Foo> newSet = ...;

private Collection difference(Collection a, Collection b) {
    Collection result = a.clone();
    result.removeAll(b)
    return result;
}

private Collection intersection(Collection a, Collection b) {
    Collection result = a.clone();
    result.retainAll(b)
    return result;
}

public doWork() {
    // if foo is in(*) oldSet but not newSet, call doRemove(foo)
    Collection removed = difference(oldSet, newSet);
    if (!removed.isEmpty()) {
        loop removed {
            Foo foo = removedIter.next();
            doRemove(foo);
        }
    }
    //else if foo is not in oldSet but in newSet, call doAdd(foo)
    Collection added = difference(newSet, oldSet);
    if (!added.isEmpty()) {
        loop added  {
            Foo foo = addedIter.next();
            doAdd(foo);
        }
    }

    // else if foo is in both collections but modified, call doUpdate(oldFoo, newFoo)
    Collection matched = intersection(oldSet, newSet);
    Comparator comp = new Comparator() {
        int compare(Object o1, Object o2) {
            Foo f1, f2;
            if (o1 instanceof Foo) f1 = (Foo)o1;
            if (o2 instanceof Foo) f2 = (Foo)o2;
            return f1.activated == f2.activated ? f1.startdate.compareTo(f2.startdate) == 0 ? ... : f1.startdate.compareTo(f2.startdate) : f1.activated ? 1 : 0;
        }

        boolean equals(Object o) {
             // equal to this Comparator..not used
        }
    }
    loop matched {
        Foo foo = matchedIter.next();
        Foo oldFoo = oldSet.get(foo);
        Foo newFoo = newSet.get(foo);
        if (comp.compareTo(oldFoo, newFoo ) != 0) {
            doUpdate(oldFoo, newFoo);
        } else {
            //else if !foo.activated && foo.startDate >= now, call doStart(foo)
            if (!foo.activated && foo.startDate >= now) doStart(foo);

            // else if foo.activated && foo.endDate <= now, call doEnd(foo)
            if (foo.activated && foo.endDate <= now) doEnd(foo);
        }
    }
}

あなたの質問に関する限り: ID をキーにして oldSet と newSet を HashMap (ここでは順序は関係ありません) に変換すると、コードが読みやすくなり、比較しやすくなりますか? 変換時に失われる時間とメモリのパフォーマンスはどれくらいですか? おそらく Map を使用してコードを読みやすくすると思いますが、おそらく変換中により多くのメモリと時間を使用するでしょう。

2 つのセットを反復して適切な操作を実行すると、より効率的かつ簡潔になりますか? はい、特に@Mike Sharekのアドバイスに従って、独自のリストを特殊な方法でローリングするか、ビジターデザインパターンのようなものに従ってコレクションを実行し、各アイテムを処理する場合、これは両方の世界で最高です。

于 2008-08-23T03:54:59.143 に答える
4

これを行う最も簡単な方法は、リストが同じタイプである限り、ApacheコレクションAPI - CollectionUtils.subtract(list1,list2) を使用することだと思います。

于 2010-06-23T18:29:09.260 に答える
2

リストに移動して、次のように解決します。

  1. リスト内のオブジェクトがComparableでない場合、カスタムComparatorを使用して ID の昇順で両方のリストを並べ替えます
  2. マージ ソート アルゴリズムのマージ フェーズのように、両方のリストの要素を反復処理しますが、リストをマージする代わりに、ロジックをチェックします。

コードは多かれ少なかれ次のようになります。

/* Main method */
private void execute(Collection<Foo> oldSet, Collection<Foo> newSet) {
  List<Foo> oldList = asSortedList(oldSet);
  List<Foo> newList = asSortedList(newSet);

  int oldIndex = 0;
  int newIndex = 0;
  // Iterate over both collections but not always in the same pace
  while( oldIndex < oldList.size() 
      && newIndex < newIndex.size())  {
    Foo oldObject = oldList.get(oldIndex);
    Foo newObject = newList.get(newIndex);

    // Your logic here
    if(oldObject.getId() < newObject.getId()) {
      doRemove(oldObject);
      oldIndex++;
    } else if( oldObject.getId() > newObject.getId() ) {
      doAdd(newObject);
      newIndex++;
    } else if( oldObject.getId() == newObject.getId() 
            && isModified(oldObject, newObject) ) {
      doUpdate(oldObject, newObject);
      oldIndex++;
      newIndex++;
    } else {
      ... 
    }
  }// while

  // Check if there are any objects left in *oldList* or *newList*

  for(; oldIndex < oldList.size(); oldIndex++ ) {
    doRemove( oldList.get(oldIndex) );  
  }// for( oldIndex )

  for(; newIndex < newList.size(); newIndex++ ) {
    doAdd( newList.get(newIndex) );
  }// for( newIndex ) 
}// execute( oldSet, newSet )

/** Create sorted list from collection 
    If you actually perform any actions on input collections than you should 
    always return new instance of list to keep algorithm simple.
*/
private List<Foo> asSortedList(Collection<Foo> data) {
  List<Foo> resultList;
  if(data instanceof List) {
     resultList = (List<Foo>)data;
  } else {
     resultList = new ArrayList<Foo>(data);
  }
  Collections.sort(resultList)
  return resultList;
}
于 2008-08-31T17:58:00.300 に答える
0
public static boolean doCollectionsContainSameElements(
        Collection<Integer> c1, Collection<Integer> c2){

    if (c1 == null || c2 == null) {
        return false;
    }
    else if (c1.size() != c2.size()) {
        return false;
    } else {    
        return c1.containsAll(c2) && c2.containsAll(c1);
    }       
}
于 2016-02-09T15:44:38.070 に答える
-1

その小さいセットの場合、配列から HashMap/セットに変換する価値は通常ありません。実際、それらを配列に保持してから、キーで並べ替え、両方のリストを同時に反復処理して比較することをお勧めします。

于 2008-08-22T20:57:38.663 に答える
-2

リストまたはセットを比較するには、 を使用できますArrays.equals(object[], object[])。値のみをチェックします。を取得するObject[]には、メソッドを使用できますCollection.toArray()

于 2010-12-17T10:05:08.330 に答える