22

アプリケーションにこのタイプがあるとしましょう:

public class A {
  public int id;
  public B b;

  public boolean equals(Object another) { return this.id == ((A)another).id; }
  public int hashCode() { return 31 * id; //nice prime number }
}

そして構造。今、私はタイプのオブジェクトを持っていて、次のことをしたいと思っています:Set<A>A

  • myAがセット内にある場合は、そのフィールドbを更新して my オブジェクトに一致させます。
  • それ以外の場合は、セットに追加します。

そのため、そこにあるかどうかを確認するのは簡単です ( contains)。また、セットに追加するのも簡単です。私の質問は次のとおりです。オブジェクトを更新するためのハンドルを取得するにはどうすればよいですか? インターフェイスにはメソッドSetがありませんget。私が考えることができる最善の方法は、セット内のオブジェクトを削除して、私のものを追加することでした。もう 1 つの、さらに悪い方法は、イテレータを使用してセットをトラバースして、オブジェクトを見つけようとすることです。

より良い提案を喜んでお受けします...これには、他のデータ構造の効率的な使用が含まれます。

ユヴァル=8-)

編集:ご回答いただきありがとうございます...残念ながらMap、この目的のためだけにコレクションのタイプを根本的に変更することは少し極端になるため、ここで a を使用することを提案する最良の回答を「受け入れる」ことはできません(このコレクションは既に Hibernate を介してマップされています...)

4

7 に答える 7

23

Set にはオブジェクトのインスタンスを 1 つしか含めることができないため (equalsおよびhashCodeメソッドで定義されているように)、それを削除してから追加するだけです。1 つが既に存在する場合、その別の 1 つがセットから削除され、必要な 1 つに置き換えられます。

私は似たようなことをするコードを持っています - 私はオブジェクトをキャッシュしているので、特定のオブジェクトが GUI のさまざまな場所に表示されますが、常に同じです。その場合、セットを使用する代わりにマップを使用し、更新を取得すると、新しいインスタンスを作成するのではなく、マップから取得してその場で更新します。

于 2008-10-06T17:03:25.920 に答える
13

Map<Integer,A>あなたは本当にではなくを使いたいのですSet<A>

次に、ID を (! にも格納されてAいますが) オブジェクトにマップします。したがって、新しい保存は次のとおりです。

A a = ...;
Map<Integer,A> map = new HashMap<Integer,A>();
map.put( a.id, a );

完全な更新アルゴリズムは次のとおりです。

public static void update( Map<Integer,A> map, A obj ) {
  A existing = map.get( obj.id );
  if ( existing == null )
     map.put( obj.id, obj );
  else
     existing.b = obj.b;
}

ただし、さらに単純な場合もあります。Aあなたが与えたもの よりも多くのフィールドがあると思います。これが当てはまらない場合、実際には a を使用するだけで、Map<Integer,B>何も折りたたまれません。

Map<Integer,B> map = new HashMap<Integer,B>();
// The insert-or-update is just this:
map.put( id, b );
于 2008-10-06T17:03:42.673 に答える
10

セットを使用している場合、削除/追加を使用するよりも簡単にできるとは思いません。

set.remove(a);
set.add(a);

一致する A が見つかった場合、それは削除され、新しいものを追加すると、if (set.contains(A))条件さえ必要ありません。

ID と更新されたフィールドを持つオブジェクトがあり、そのオブジェクトの他の側面をあまり気にしない場合は、破棄して置き換えてください。

その ID に一致する A に対して他に何かする必要がある場合は、セットを反復処理してそれを見つけるか、別のコンテナーを使用する必要があります (ジェイソンが提案したマップのように)。

于 2008-10-06T18:54:37.380 に答える
5

これについてはまだ誰も言及していませんが、変更可能なプロパティに基づいhashCodeたり、それに基づいたりequalsすることは、すべきでない非常に大きなことの 1 つです。コンストラクターを離れた後、オブジェクト ID をいじらないでください。そうすると、後でバグを特定するのが非常に困難になる可能性が大幅に高まります。バグに見舞われなくても、依存するすべてのデータ構造を常に適切に更新し、equals一貫性を維持するための会計作業hashCodeは、オブジェクトの ID を変更するだけでできるという認識されている利点をはるかに上回ります。あなたが走るように。

代わりに、コンストラクターを介して id を渡すことを強くお勧めします。それを変更する必要がある場合は、A の新しいインスタンスを作成します。これにより、オブジェクトのユーザー (自分自身を含む) がコレクション クラス (および多くのその他)equalsおよびの不変の動作に依存するものhashCode

于 2008-10-07T03:33:59.750 に答える
2

Map はどうですか<A,A> 冗長であることは承知していますが、希望どおりの動作が得られると思います。Set に get(Object o) メソッドがあることを本当に望んでいます。

于 2008-10-06T18:19:46.733 に答える
0

ASet というデコレータを生成し、内部 Map をバッキング データ構造として使用することができます。

class ASet {
 private Map<Integer, A> map;
 public ASet() {
  map = new HashMap<Integer, A>();
 }

 public A updateOrAdd(Integer id, int delta) {
   A a = map.get(a);
   if(a == null) {
    a = new A(id);
    map.put(id,a);
   }
   a.setX(a.getX() + delta);
 }
}

また、Trove API を参照することもできます。これは、パフォーマンスと、プリミティブ変数を使用しているというアカウンティングの点では優れていますが、この機能を非常にうまく公開しています (例: map.adjustOrPutValue(key, initialValue, deltaValue))。

于 2008-10-06T18:42:29.480 に答える
0

少し範囲外ですが、hashCode() を再実装するのを忘れていました。equals をオーバーライドする場合は、例であっても hashCode() をオーバーライドしてください。

例えば; HashSet は Object の hashCode を使用してバケット (ビジネス ロジックとは関係のない数値) を特定し、その中の要素のみを equals() するため、Set の HashSet 実装がある場合、contains() はおそらく間違っています。バケツ。

public class A {
  public int id;
  public B b;
  public int hashCode() {return id;} // simple and efficient enough for small Sets 
  public boolean equals(Object another) { 
    if (object == null || ! (object instanceOf A) ) {
      return false;
    }
    return this.id == ((A)another).id; 
   }
}
public class Logic {
  /**
   * Replace the element in data with the same id as element, or add element
   * to data when the id of element is not yet used by any A in data. 
   */
  public void update(Set<A> data, A element) {
    data.remove(element); // Safe even if the element is not in the Set
    data.add(element); 
  }
}

EDIT Yuval は、Set.add が既存の要素を上書きしないことを正しく示しましたが、要素がまだコレクションにない場合にのみ追加します ("is" は equals によって実装されています)。

于 2008-10-06T17:33:21.040 に答える