我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
当前回答
static <K extends Comparable<? super K>, V extends Comparable<? super V>>
Map sortByValueInDescendingOrder(final Map<K, V> map) {
Map re = new TreeMap(new Comparator<K>() {
@Override
public int compare(K o1, K o2) {
if (map.get(o1) == null || map.get(o2) == null) {
return -o1.compareTo(o2);
}
int result = -map.get(o1).compareTo(map.get(o2));
if (result != 0) {
return result;
}
return -o1.compareTo(o2);
}
});
re.putAll(map);
return re;
}
@Test(timeout = 3000l, expected = Test.None.class)
public void testSortByValueInDescendingOrder() {
char[] arr = "googler".toCharArray();
Map<Character, Integer> charToTimes = new HashMap();
for (int i = 0; i < arr.length; i++) {
Integer times = charToTimes.get(arr[i]);
charToTimes.put(arr[i], times == null ? 1 : times + 1);
}
Map sortedByTimes = sortByValueInDescendingOrder(charToTimes);
Assert.assertEquals(charToTimes.toString(), "{g=2, e=1, r=1, o=2, l=1}");
Assert.assertEquals(sortedByTimes.toString(), "{o=2, g=2, r=1, l=1, e=1}");
Assert.assertEquals(sortedByTimes.containsKey('a'), false);
Assert.assertEquals(sortedByTimes.get('a'), null);
Assert.assertEquals(sortedByTimes.get('g'), 2);
Assert.assertEquals(sortedByTimes.equals(charToTimes), true);
}
其他回答
我们只需像这样对地图进行排序
Map<String, String> unsortedMap = new HashMap<String, String>();
unsortedMap.put("E", "E Val");
unsortedMap.put("F", "F Val");
unsortedMap.put("H", "H Val");
unsortedMap.put("B", "B Val");
unsortedMap.put("C", "C Val");
unsortedMap.put("A", "A Val");
unsortedMap.put("G", "G Val");
unsortedMap.put("D", "D Val");
Map<String, String> sortedMap = new TreeMap<String, String>(unsortedMap);
System.out.println("\nAfter sorting..");
for (Map.Entry <String, String> mapEntry : sortedMap.entrySet()) {
System.out.println(mapEntry.getKey() + " \t" + mapEntry.getValue());
对键进行排序需要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();
最后,如果您需要连续访问已排序的信息(而不是偶尔排序一次),可以使用额外的多重映射。如果你需要更多细节,请告诉我。。。
您可以尝试Guava的多功能地图:
TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
Multimaps.invertFrom(Multimaps.forMap(originalMap),
ArrayListMultimap.<Integer, String>create()).asMap());
因此,您将获得从原始值到对应于它们的键集合的映射。即使同一值有多个键,也可以使用此方法。
创建自定义比较器,并在创建新的TreeMap对象时使用它。
class MyComparator implements Comparator<Object> {
Map<String, Integer> map;
public MyComparator(Map<String, Integer> map) {
this.map = map;
}
public int compare(Object o1, Object o2) {
if (map.get(o2) == map.get(o1))
return 1;
else
return ((Integer) map.get(o2)).compareTo((Integer)
map.get(o1));
}
}
在主函数中使用以下代码
Map<String, Integer> lMap = new HashMap<String, Integer>();
lMap.put("A", 35);
lMap.put("B", 75);
lMap.put("C", 50);
lMap.put("D", 50);
MyComparator comparator = new MyComparator(lMap);
Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
newMap.putAll(lMap);
System.out.println(newMap);
输出:
{B=75, D=50, C=50, A=35}
这种方法正好能达到目的。(“挫折”是Values必须实现java.util.Comparable接口)
/**
* Sort a map according to values.
* @param <K> the key of the map.
* @param <V> the value to sort according to.
* @param mapToSort the map to sort.
* @return a map sorted on the values.
*/
public static <K, V extends Comparable< ? super V>> Map<K, V>
sortMapByValues(final Map <K, V> mapToSort)
{
List<Map.Entry<K, V>> entries =
new ArrayList<Map.Entry<K, V>>(mapToSort.size());
entries.addAll(mapToSort.entrySet());
Collections.sort(entries,
new Comparator<Map.Entry<K, V>>()
{
@Override
public int compare(
final Map.Entry<K, V> entry1,
final Map.Entry<K, V> entry2)
{
return entry1.getValue().compareTo(entry2.getValue());
}
});
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : entries)
{
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
http://javawithswaranga.blogspot.com/2011/06/generic-method-to-sort-hashmap.html