由于值不是唯一的,我发现自己需要将keySet转换为数组,并通过使用自定义比较器对该数组进行排序,以便根据与键关联的值进行排序。
有没有更简单的方法?
以下是通用版本:
public class MapUtil {
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
list.sort(Entry.comparingByValue());
Map<K, V> result = new LinkedHashMap<>();
for (Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
forEachOrdered
而不是 forEach
,因为 forEach
的文档中注明:“此操作的行为明确是不确定的。” - robJava 8 提供了一个新的解决方案:将 Map 的 entry 转换成一个流,并使用来自 Map.Entry 的比较器组合器:
Java 8提供了一种新的解决方法:将Map的条目转换为流,并使用来自Map.Entry的比较器组合器。Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue());
这将让您以数值升序消耗条目。如果您想要降序值,只需反转比较器:
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
如果这些值不能进行比较,您可以传递一个显式的比较器:
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(comparator));
然后,您可以继续使用其他流操作来消耗数据。例如,如果您想在新地图中获取前10个:
Map<K,V> topTen =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(10)
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
上面显示的LinkedHashMap
按照插入的顺序迭代条目。
或者打印到System.out
:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.forEach(System.out::println);
这段代码可能会出现多种问题。如果您打算使用提供的代码,请务必仔细阅读评论,以了解其影响。例如,无法通过键检索值(get
始终返回 null
)。
以下是使用 TreeMap 的简单方法:
public class Testing {
public static void main(String[] args) {
HashMap<String, Double> map = new HashMap<String, Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
map.put("A", 99.5);
map.put("B", 67.4);
map.put("C", 67.4);
map.put("D", 67.3);
System.out.println("unsorted map: " + map);
sorted_map.putAll(map);
System.out.println("results: " + sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with
// equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
输出:
unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}
return ((Comparable)base.get(a)).compareTo(((Comparable)base.get(b)))
吗? - Stephenmap.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
- steffenvalueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
这将创建一个函数(对象)用于地图[以任何键作为输入,返回相应的值],然后对它们[值]应用自然(可比较)排序。
如果它们不可比较,则需要做类似以下的操作:
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= 将自然排序应用于键映射的值,并将其与键的自然排序结合起来
请注意,如果您的键与0进行比较,则仍无法工作,但这对于大多数可比较项(例如hashCode
、equals
和compareTo
通常是同步的...)应该足够。
请参见Ordering.onResultOf()和Functions.forMap()。
现在我们有了一个按照我们想要的方式执行比较的比较器,我们需要从中获取结果。
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
现在这个方法很可能可以工作,但是:
TreeMap
上尝试上面的比较器;当插入的键没有值时,尝试比较它是没有意义的,因为在put之后才会有值,即它会非常快地崩溃对我来说,第1点有点成为瓶颈;Google集合非常懒惰(这很好:你几乎可以在瞬间完成几乎所有操作;真正的工作是在你开始使用结果时完成的),这需要复制一个完整的地图!
不过别担心;如果你非常着迷于拥有这种方式排序的“实时”映射,你可以使用以下疯狂的东西解决上述两个问题中的一个或两个问题!
注意:这在2012年6月发生了重大变化——以前的代码永远无法工作:需要内部HashMap来查找值,而不会在TreeMap.get()
-> compare()
和compare()
-> get()
之间创建无限循环
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
//A map for doing lookups on the keys for comparison so we don't get infinite loops
private final Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
this(partialValueOrdering, new HashMap<K,V>());
}
private ValueComparableMap(Ordering<? super V> partialValueOrdering,
HashMap<K, V> valueMap) {
super(partialValueOrdering //Apply the value ordering
.onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
.compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
this.valueMap = valueMap;
}
public V put(K k, V v) {
if (valueMap.containsKey(k)){
//remove the key in the sorted set before adding the key again
remove(k);
}
valueMap.put(k,v); //To get "real" unsorted values for the comparator
return super.put(k, v); //Put it in value order
}
public static void main(String[] args){
TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
map.put("a", 5);
map.put("b", 1);
map.put("c", 3);
assertEquals("b",map.firstKey());
assertEquals("a",map.lastKey());
map.put("d",0);
assertEquals("d",map.firstKey());
//ensure it's still a map (by overwriting a key, but with a new value)
map.put("d", 2);
assertEquals("b", map.firstKey());
//Ensure multiple values do not clobber keys
map.put("e", 2);
assertEquals(5, map.size());
assertEquals(2, (int) map.get("e"));
assertEquals(2, (int) map.get("d"));
}
}
Map<V,K>
)。 new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
@Override
public boolean containsKey(Object key) {
return valueMap.containsKey(key);
}
- has981从http://www.programmersheaven.com/download/49349/download.aspx下载
private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Object>() {
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}
使用Java 8,你可以使用流API以更简洁的方式来实现:
Map<K, V> sortedMap = map.entrySet().stream()
.sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
comparing(Entry::getValue).reversed()
的意思是“按值进行比较并倒序排列”。 - assyliasCollections.reverseOrder(comparing(Entry::getValue))
。该代码会按照 Entry
的值进行排序,并返回一个逆序的比较器。 - Vlad HolubievEntry.comparingByValue(Comparator.reverseOrder())
。 - Gediminas Rimsa排序键需要比较器查找每个比较的值。更可扩展的解决方案是直接使用entrySet,因为那样每个比较的值将立即可用(尽管我没有通过数字来支持这一点)。
以下是这种解决方案的通用版本:
public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
final int size = map.size();
final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
list.addAll(map.entrySet());
final ValueComparator<V> cmp = new ValueComparator<V>();
Collections.sort(list, cmp);
final List<K> keys = new ArrayList<K>(size);
for (int i = 0; i < size; i++) {
keys.set(i, list.get(i).getKey());
}
return keys;
}
private static final class ValueComparator<V extends Comparable<? super V>>
implements Comparator<Map.Entry<?, V>> {
public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
有办法减少上述解决方案中的内存旋转。例如,可以重新使用创建的第一个ArrayList作为返回值;这将需要抑制一些泛型警告,但对于可重用的库代码而言可能是值得的。此外,Comparator不必在每次调用时重新分配。
以下是更高效但不太吸引人的版本:
public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
final int size = map.size();
final List reusedList = new ArrayList(size);
final List<Map.Entry<K, V>> meView = reusedList;
meView.addAll(map.entrySet());
Collections.sort(meView, SINGLE);
final List<K> keyView = reusedList;
for (int i = 0; i < size; i++) {
keyView.set(i, meView.get(i).getKey());
}
return keyView;
}
private static final Comparator SINGLE = new ValueComparator();
最后,如果您需要持续访问排序的信息(而不只是偶尔进行排序),您可以使用额外的多重映射。如果您需要更多详细信息,请告诉我...
commons-collections库包含一个名为 TreeBidiMap 的解决方案。或者,您可以查看Google Collections API。它有一个名为 TreeMultimap 的工具,您也可以使用它。
如果您不想使用这些框架...它们提供了源代码。
我看了给出的答案,但很多答案比必要的复杂或在键值相同时移除地图元素。
这里是我认为更适合的解决方案:
public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
int compare = map.get(k2).compareTo(map.get(k1));
if (compare == 0) return 1;
else return compare;
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
请注意,该地图是按照从高到低排序的。
return new LinkedHashMap(sortedByValues);
- Erel Segal-Halevi给定地图
Map<String, Integer> wordCounts = new HashMap<>();
wordCounts.put("USA", 100);
wordCounts.put("jobs", 200);
wordCounts.put("software", 50);
wordCounts.put("technology", 70);
wordCounts.put("opportunity", 200);
根据值升序排序地图
Map<String,Integer> sortedMap = wordCounts.entrySet().
stream().
sorted(Map.Entry.comparingByValue()).
collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
System.out.println(sortedMap);
按值从大到小对地图进行排序
Map<String,Integer> sortedMapReverseOrder = wordCounts.entrySet().
stream().
sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
System.out.println(sortedMapReverseOrder);
输出:
{软件=50,技术=70,美国=100,工作=200,机会=200}
{工作=200,机会=200,美国=100,技术=70,软件=50}
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
和Collections.sort ....
进行排序。 - Hannes