6

同じ値が存在するキーを取得する簡単な方法はありますか? またはもっと重要なことに、同じ値が複数回出現する回数を取得するにはどうすればよいですか?

ハッシュマップを考えてみましょう:

1->A
2->A
3->A
4->B
5->C
6->D
7->D

ここで、同じ値が複数回発生しました(A 2回、D 1回)。それ(3)が見返りに欲しいものです。

keyset/map.values() リストでハッシュマップを反復処理できますが、そのようにするのは非常に面倒です。提案や解決策はありますか?

編集: 私の文脈は、私はタイムテーブルジェネレーターに取り組んでいます。タイムスロットのデータ構造は

{String day-hour, HashMap<String,Event> Rooms}

1 日の時間については、いくつかのイベントがルーム マップに割り当てられます。ソリューションの適合性をチェックしながら、1 人のスタッフが同じ時間に複数のイベントを割り当てられているかどうかを知る必要があります。したがって、値 Event.getStaff() によって部屋マップにいくつの違反があるかを確認したいと思います。

編集: ここでは値はオブジェクトです。同じオブジェクトの出現をカウントしたくありません。オブジェクトのフィールドです。EVENT オブジェクトにはフィールドスタッフがあり、複数のスタッフの出現をカウントする必要があります。

4

5 に答える 5

5

keyset/map.values() リストでハッシュマップを反復処理できますが、そのようにするのは非常に面倒です。

それは非効率的ですが、値のキーへの逆マッピングを格納するための何らかのマルチマップがなければ、それについてできることはあまりありません。

ただし、 Guavaを使用する場合、コードに関して面倒である必要はありません。

Multiset<String> counts = HashMultiSet.create(map.values());
for (Multiset.Entry<String> entry : counts.entrySet) {
  if (entry.getCount() > 1) {
    System.out.println(entry.getElement() + ": " + entry.getCount());
  }
}
于 2012-11-07T12:17:42.810 に答える
3

これは私が考える素晴らしい方法です:

int freq = Collections.frequency(map.values(), "A");

あなたの例では「3」を返します。乾杯!

編集:申し訳ありませんが、最初の試みで質問を誤解しました。これでうまくいくはずです:

int k = 0;
Set<String> set = new HashSet<String>(map.values());
for (String s : set) {
   int i = Collections.frequency(map.values(), s);
   k += i > 1 ? i - 1 : 0;
}

ただし、実際のキーを取得することはできません。しかし、それは最も重要なことではありませんでしたよね?

于 2012-11-07T12:21:17.713 に答える
1

どうですか(ジョンの答えを拡大)

Multiset<V> counts = HashMultiSet.create(map.values());
Predicate<Map.Entry<K,V>> pred = new Predicate<Map.Entry<K,V>>(){
   public boolean apply(Map.Entry<K,V> entry){
       return counts.count(entry.getValue()) > 1;
   }
}
Map<K,V> result = Maps.filterEntries(map, pred);

これにより、各キーが複製された値にマップされるマップが作成されます。

この回答は、重複する値を持つキーを取得するために、質問の最初の部分 (「重要度の低い部分」) に対処するためにのみ必要です。

于 2012-11-07T12:31:31.123 に答える
0

コンテキストはわかりませんが、マルチマップを使用するとどうなりますか:

Map<String, List<Integer>>

このようにすると、マップは次のようになります。

A->1, 2, 3
B->4
C->5
D->6, 7
于 2012-11-07T12:18:42.823 に答える
0

(Hash)Map の周りにラッパー クラスを作成し、put()-remove() メソッドをデコレートして、元の Map の値がキーであり、値が出現回数である別のマップを維持することができます。次に、それをクエリするメソッドを実装するだけです...

しかし、これはかなりトリッキーです!マップに存在しないオブジェクトへのリンクを作成しないように注意する必要があります...これにより、メモリ リークが発生する可能性があります。

また、null 値の許容範囲をカウントする必要があります...

public static class MyCountingMap<K,V> implements Map<K,V> {
  private final Map<K,V> internalMap;
  //hashmap tolerates null as a key!
  private final Map<V,Long> counterMap = new HashMap<V, Long>();

  public MyCountingMap(Map<K, V> internalMap) {
    super();
    this.internalMap = internalMap;
  }


  @Override
  public V put(K key, V value) {
    boolean containedOriginally = internalMap.containsKey(key);

    V origValue = internalMap.put(key, value);

    updateCounterPut(containedOriginally, origValue, value);

    return origValue;
  }

  @Override
  public void putAll(Map<? extends K, ? extends V> m) {
    //now this is the awkward part...
    //this whole thing could be done through a loop and the put() method, 
    //but I'd prefer to use the original implementation...
    for(Map.Entry<? extends K, ? extends V> entry :m.entrySet()) {
      boolean containedOriginally = internalMap.containsKey(entry.getKey());
      V origValue = internalMap.get(entry.getKey());
      updateCounterPut(containedOriginally, origValue, entry.getValue());
    }


    internalMap.putAll(m);
  }

  // this method updates the counter
  private void updateCounterPut(boolean containedOriginally, V origValue, V newValue) {
    //if it was in the map, and it is different than the original, decrement 
    if(containedOriginally && isDifferent(origValue, newValue)) 
    {
      decrement(origValue);
    }

    //if it was NOT in the map, or the value differs
    if(!containedOriginally || isDifferent(origValue, newValue)) {
      increment(newValue);
    }
  }

  // nothing special, just nicer to extract this to a method. Checks if the two values are the same or not.
  private static boolean isDifferent(Object origValue, Object newValue) {
    return ((origValue==null && newValue!=null) || !(origValue!=null && origValue.equals(newValue)));
  }

  //this method returns the counter value for the map value
  public Long getValueCount(V value) {
    return counterMap.get(value);
  }

  @Override
  public V remove(Object key) {
    V toReturn = internalMap.remove(key);
    if(toReturn!=null) {
      decrement(toReturn);
    }
    return toReturn;
  }

  private void increment(V value) {
    Long count = counterMap.get(value);
    if(count == null) {
      count = 0L;
    }
    counterMap.put(value, count+1);
  }

  private void decrement(V value) {
    Long count = counterMap.get(value);
    if(count == null) {
      count = 0L;
    }

    //last! Have to remove reference to prevent memory leak!!
    if(count == 1L) {
      counterMap.remove(value);
    } else {
      counterMap.put(value, count-1);
    }

  }
  //... boring wrapper methods ...
  public void clear() { internalMap.clear(); }
  public boolean containsKey(Object key) { return internalMap.containsKey(key); }
  public boolean containsValue(Object value) { return internalMap.containsValue(value);   }
  public Set<Entry<K, V>> entrySet() { return internalMap.entrySet();    }
  public V get(Object key) { return internalMap.get(key); }
  public boolean isEmpty() { return internalMap.isEmpty(); } 
  public Set<K> keySet() { return internalMap.keySet(); }
  public int size() { return internalMap.size(); }
  public Collection<V> values() { return internalMap.values(); }
}
于 2012-11-07T12:19:02.057 に答える