我有一个String-> Integer的大地图,我想找到地图中最高的5个值。我当前的方法是将映射转换为pair(key,value)对象的数组列表,然后在获取第一个5之前使用Collections.sort()进行排序。一个键可以在操作过程中更新其值
我认为这种方法是可以接受的单线程,但是如果我有多个线程全部触发转置并经常排序它似乎不是很有效。替代方案似乎是维护最高5个条目的单独列表,并在地图上的相关操作发生时保持更新。
我可以提供一些优化的建议/替代方案吗?如果有好处,我很乐意考虑不同的数据结构。
谢谢!
答案 0 :(得分:5)
好吧,要在Map中找到最高的5个值,您可以在O(n)
时间内执行此操作,其中任何排序都比此慢。
最简单的方法是简单地通过Map的入口集进行for循环。
for (Entry<String, Integer> entry: map.entrySet()) {
if (entry.getValue() > smallestMaxSoFar)
updateListOfMaximums();
}
答案 1 :(得分:3)
您可以使用两张地图:
// Map name to value
Map<String, Integer> byName
// Maps value to names
NavigableMap<Integer, Collection<String>> byValue
并确保始终保持同步(可能在另一个负责put,get等的类中包装)。对于最高值,请使用byValue.navigableKeySet().descendingIterator()
。
答案 2 :(得分:2)
我认为这种方法是可以接受的单线程,但是如果我有多个线程全部触发转置并经常排序它似乎不是很有效。替代方案似乎是维护最高5个条目的单独列表,并在地图上的相关操作发生时保持更新。
你可以采取一种方法。当线程请求地图的“已排序视图”时,创建地图的副本,然后处理该地图的排序。
public List<Integer> getMaxFive() {
Map<String, Integer> copy = null;
synchronized(lockObject) {
copy = new HashMap<String, Integer>(originalMap);
}
//sort the copy as usual
return list;
}
理想情况下,如果您有多个线程访问某些状态(例如此映射),则将该状态封装在其他类的后面,以便每个线程不直接更新映射。
答案 3 :(得分:1)
我会创建一个方法,如:
private static int[] getMaxFromMap(Map<String, Integer> map, int qty) {
int[] max = new int[qty];
for (int a=0; a<qty; a++) {
max[a] = Collections.max(map.values());
map.values().removeAll(Collections.singleton(max[a]));
if (map.size() == 0)
break;
}
return max;
}
利用Collections.max()
和Collections.singleton()
答案 4 :(得分:1)
有两种方法可以轻松完成:
n
元素。n
最高值列表。如果您想要检索未知或大量的最高值,则第一种方法是可行的方法。如果要检索固定的少量值,对于某些程序员来说,第二个可能更容易理解。 就个人而言,我更喜欢第一种方法。
答案 5 :(得分:0)
请尝试其他数据结构。假设有一个名为MyClass的类,其属性为key(String)和value(int)。当然,MyClass需要实现Comparable接口。另一种方法是创建一个名为MyClassComparator的类,它扩展了Comparator。
compareTo(无论它在哪里)方法应该像这样定义: 的compareTo(参数){ return value2 - value1; //下降 }
其余的很容易。使用List并调用Collections.sort(参数)方法将执行排序部分。
我不知道Collections.sort(参数)使用的排序算法。但是如果您觉得某些数据可能会随着时间的推移而出现,则需要插入排序。因为它对于几乎排序的数据是好的,它是online。
答案 6 :(得分:0)
如果修改很少,我会实现一些SortedByValHashMap<K,V> extends HashMap <K,V>
,类似于LinkedHashMap
),以保持按值排序的条目。