我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
当前回答
您可以尝试Guava的多功能地图:
TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
Multimaps.invertFrom(Multimaps.forMap(originalMap),
ArrayListMultimap.<Integer, String>create()).asMap());
因此,您将获得从原始值到对应于它们的键集合的映射。即使同一值有多个键,也可以使用此方法。
其他回答
对键进行排序需要Comparator为每个比较查找每个值。一个更具可扩展性的解决方案将直接使用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();
最后,如果您需要连续访问已排序的信息(而不是偶尔排序一次),可以使用额外的多重映射。如果你需要更多细节,请告诉我。。。
这太复杂了。地图不应该按价值排序。最简单的方法是创建自己的类,以满足您的需求。
在下面的示例中,您应该在*所在的位置添加TreeMap比较器。但通过javaAPI,它只提供比较器键,而不提供值。此处所述的所有示例均基于2个地图。一个哈希和一个新树。这很奇怪。
示例:
Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);
因此,通过以下方式将地图更改为集合:
ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);
您将创建类Results,
public class Results {
private Driver driver;
private Float time;
public Results(Driver driver, Float time) {
this.driver = driver;
this.time = time;
}
public Float getTime() {
return time;
}
public void setTime(Float time) {
this.time = time;
}
public Driver getDriver() {
return driver;
}
public void setDriver (Driver driver) {
this.driver = driver;
}
}
以及Comparator类:
public class ResultsComparator implements Comparator<Results> {
public int compare(Results t, Results t1) {
if (t.getTime() < t1.getTime()) {
return 1;
} else if (t.getTime() == t1.getTime()) {
return 0;
} else {
return -1;
}
}
}
这样,您可以轻松添加更多依赖项。
最后一点,我将添加简单迭代器:
Iterator it = set.iterator();
while (it.hasNext()) {
Results r = (Results)it.next();
System.out.println( r.getDriver().toString
//or whatever that is related to Driver class -getName() getSurname()
+ " "
+ r.getTime()
);
}
如果倾向于使用一个Map数据结构,该结构可以按值进行固有排序,而不必触发任何排序方法或显式传递给实用程序,则以下解决方案可能适用:
(1) org.rools.chance.core.util.ValueSortedMap(JBoss项目)在内部维护两个映射,一个用于查找,另一个用于维护排序值。与之前添加的答案非常相似,但可能是抽象和封装部分(包括复制机制)使其更安全地从外部使用。
(2) http://techblog.molindo.at/2008/11/java-map-sorted-by-value.html避免维护两个映射,而是依赖/扩展Apache Common的LinkedMap。(博客作者注:这里的所有代码都在公共领域):
// required to access LinkEntry.before and LinkEntry.after
package org.apache.commons.collections.map;
// SNIP: imports
/**
* map implementation based on LinkedMap that maintains a sorted list of
* values for iteration
*/
public class ValueSortedHashMap extends LinkedMap {
private final boolean _asc;
// don't use super()!
public ValueSortedHashMap(final boolean asc) {
super(DEFAULT_CAPACITY);
_asc = asc;
}
// SNIP: some more constructors with initial capacity and the like
protected void addEntry(final HashEntry entry, final int hashIndex) {
final LinkEntry link = (LinkEntry) entry;
insertSorted(link);
data[hashIndex] = entry;
}
protected void updateEntry(final HashEntry entry, final Object newValue) {
entry.setValue(newValue);
final LinkEntry link = (LinkEntry) entry;
link.before.after = link.after;
link.after.before = link.before;
link.after = link.before = null;
insertSorted(link);
}
private void insertSorted(final LinkEntry link) {
LinkEntry cur = header;
// iterate whole list, could (should?) be replaced with quicksearch
// start at end to optimize speed for in-order insertions
while ((cur = cur.before) != header & amp; & amp; !insertAfter(cur, link)) {}
link.after = cur.after;
link.before = cur;
cur.after.before = link;
cur.after = link;
}
protected boolean insertAfter(final LinkEntry cur, final LinkEntry link) {
if (_asc) {
return ((Comparable) cur.getValue())
.compareTo((V) link.getValue()) & lt; = 0;
} else {
return ((Comparable) cur.getValue())
.compareTo((V) link.getValue()) & gt; = 0;
}
}
public boolean isAscending() {
return _asc;
}
}
(3) 编写一个自定义映射或从LinkedHashMap扩展,该映射仅在枚举期间根据需要进行排序(例如,values()、keyset()、entryset())。内部实现/行为是从使用该类的实现/行为中抽象出来的,但在该类的客户端看来,当请求枚举时,值总是被排序的。如果所有的put操作都在枚举之前完成,这个类希望排序只发生一次。排序方法采用了前面对这个问题的一些回答。
public class SortByValueMap<K, V> implements Map<K, V> {
private boolean isSortingNeeded = false;
private final Map<K, V> map = new LinkedHashMap<>();
@Override
public V put(K key, V value) {
isSortingNeeded = true;
return map.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
isSortingNeeded = true;
map.putAll(map);
}
@Override
public Set<K> keySet() {
sort();
return map.keySet();
}
@Override
public Set<Entry<K, V>> entrySet() {
sort();
return map.entrySet();
}
@Override
public Collection<V> values() {
sort();
return map.values();
}
private void sort() {
if (!isSortingNeeded) {
return;
}
List<Entry<K, V>> list = new ArrayList<>(size());
for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry<K, V> entry = it.next();
list.add(entry);
it.remove();
}
Collections.sort(list);
for (Entry<K, V> entry : list) {
map.put(entry.getKey(), entry.getValue());
}
isSortingNeeded = false;
}
@Override
public String toString() {
sort();
return map.toString();
}
}
(4) Guava提供了ImmutableMap.Builder.orderEntriesByValue(Comparator valueComparator),尽管生成的映射是不可变的:
将此生成器配置为根据指定的比较器。排序顺序是稳定的,也就是说,如果两个条目的值作为等价项进行比较,首先插入的条目将是第一个按照构建映射的迭代顺序。
在Java 8及以上版本中对任何地图进行排序的简单方法
Map<String, Object> mapToSort = new HashMap<>();
List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());
Collections.sort(list, Comparator.comparing(o -> o.getValue().getAttribute()));
HashMap<String, Object> sortedMap = new LinkedHashMap<>();
for (Map.Entry<String, Object> map : list) {
sortedMap.put(map.getKey(), map.getValue());
}
如果您使用的是Java 7及以下版本
Map<String, Object> mapToSort = new HashMap<>();
List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Object>>() {
@Override
public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
return o1.getValue().getAttribute().compareTo(o2.getValue().getAttribute());
}
});
HashMap<String, Object> sortedMap = new LinkedHashMap<>();
for (Map.Entry<String, Object> map : list) {
sortedMap.put(map.getKey(), map.getValue());
}
如果您的Map值实现Comparable(例如String),那么这应该会起作用
Map<Object, String> map = new HashMap<Object, String>();
// Populate the Map
List<String> mapValues = new ArrayList<String>(map.values());
Collections.sort(mapValues);
如果映射值本身没有实现Comparable,但您有一个Comparable实例可以对它们进行排序,请将最后一行替换为:
Collections.sort(mapValues, comparable);