有没有简单的方法来获取存在相同值的键? 或者更重要的是,如何才能获得相同值多于一次的次数?
考虑一下hashmap:
1->A
2->A
3->A
4->B
5->C
6->D
7->D
这里相同值多于一次发生3次(两次,D次一次)。那是(3)我想要的回报。
我可以通过keyset / map.values()列表遍历hashmap,但这样做似乎相当麻烦。有什么建议或解决方案吗?
编辑: 我的背景是,我正在研究时间表生成器。时隙的数据结构是
{String day-hour, HashMap<String,Event> Rooms}
对于一天的工作时间,会在客房地图上指定一些事件。在检查解决方案的适用性时,我需要知道是否在同一时间为一名员工分配了多个事件。因此,我想通过值Event.getStaff()来检查房间地图中有多少违规。
编辑: 值是这里的对象,我不想计算相同对象的出现次数,而是计算对象的字段。 EVENT对象有一个字段员工,我需要计算多次出现的员工。
答案 0 :(得分:5)
我可以通过keyset / map.values()列表遍历hashmap,但这样做似乎很麻烦。
嗯,效率低下,但是没有太多可以做的,没有某种多地图来存储值的反向映射。
如果您使用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());
}
}
答案 1 :(得分:2)
这是我认为很好的方式:
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;
}
尽管如此,您仍然无法检索实际的密钥。但这不是最重要的事情,对吧?
答案 2 :(得分:1)
如何(扩展Jon的回答)
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);
这将生成一个映射,其中每个键都映射到一个重复的值。
这个答案只需要解决问题的第一部分(“不太重要的部分”),以获得具有重复值的密钥。
答案 3 :(得分:0)
我不知道上下文,但是如果你使用multimap会怎么样:
Map<String, List<Integer>>
所以这样你的地图就像这样:
A->1, 2, 3
B->4
C->5
D->6, 7
答案 4 :(得分:0)
你可以创建一个围绕(Hash)Map的包装类,通过装饰put() - remove()方法来维护另一个map,其中原始Map的值是键,值是数字发生。然后你只需要实现该方法来查询...
然而,这是相当棘手的!你必须要小心,不要再链接到不在地图中的对象......这可能会导致内存泄漏!
此外,必须将空值容差纳入计数......
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(); }
}