我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
当前回答
最好的方法是将HashMap转换为TreeMap。TreeMap自己排序键。如果您希望对值进行排序,则可以快速修复,如果您的值不重复,则可以使用键切换值。
其他回答
好的,这个版本使用两个新的Map对象和两次迭代,并对值进行排序。希望,虽然地图条目必须循环两次,但表现良好:
public static void main(String[] args) {
Map<String, String> unsorted = new HashMap<String, String>();
unsorted.put("Cde", "Cde_Value");
unsorted.put("Abc", "Abc_Value");
unsorted.put("Bcd", "Bcd_Value");
Comparator<String> comparer = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}};
System.out.println(sortByValue(unsorted, comparer));
}
public static <K, V> Map<K,V> sortByValue(Map<K, V> in, Comparator<? super V> compare) {
Map<V, K> swapped = new TreeMap<V, K>(compare);
for(Entry<K,V> entry: in.entrySet()) {
if (entry.getValue() != null) {
swapped.put(entry.getValue(), entry.getKey());
}
}
LinkedHashMap<K, V> result = new LinkedHashMap<K, V>();
for(Entry<V,K> entry: swapped.entrySet()) {
if (entry.getValue() != null) {
result.put(entry.getValue(), entry.getKey());
}
}
return result;
}
该解决方案使用带有比较器的TreeMap,并对所有空键和值进行排序。首先,使用TreeMap中的排序功能对值进行排序,然后使用排序后的Map创建一个结果,因为LinkedHashMap保留了相同的值顺序。
格里兹,GHad
如果没有大于地图大小的值,可以使用数组,这应该是最快的方法:
public List<String> getList(Map<String, Integer> myMap) {
String[] copyArray = new String[myMap.size()];
for (Entry<String, Integer> entry : myMap.entrySet()) {
copyArray[entry.getValue()] = entry.getKey();
}
return Arrays.asList(copyArray);
}
我认为最好的方法是使用特殊的数据结构。您可以考虑TreeMap,但在一般情况下,值可能不是唯一的。因此,您的选择是PriorityQueue:
public static <K, V> Iterator<Map.Entry<K, V>> sortByValue(
Map<K, V> map,
Comparator<V> valueComparator) {
Queue<Map.Entry<K, V>> queue = new PriorityQueue<>((one, two) ->
valueComparator.compare(one.getValue(), two.getValue()));
queue.addAll(map.entrySet());
return queue.iterator();
}
这还有一个额外的好处,即可以使用Java8进行升序或降序排序
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Utils {
public static Map<String, Integer> sortMapBasedOnValues(Map<String, Integer> map, boolean descending) {
int multiplyBy = (descending) ? -1: 1;
Map<String, Integer> sorted = map.entrySet().stream()
.sorted(comparingInt(e -> multiplyBy * e.getValue() ))
.collect(toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError();},
LinkedHashMap::new
));
return sorted;
}
}
当你有两个相等的项目时,投票给最多的答案不起作用。TreeMap保留相等的值。
示例:未排序地图
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
后果
key/value: A/99.5 key/value: C/67.5 key/value: B/67.4 key/value: D/67.3
所以省略了E!!
对我来说,它可以很好地调整比较器,如果它等于,则不返回0,而是返回-1。
在示例中:
类ValueComparator实现Comparator{地图库;公共ValueComparator(地图库){this.base=基数;}public int compare(对象a,对象b){如果((双)base.get(a)<(双)base.get(b)){返回1;}否则如果((双)base.get(a)==(双)base.get(b)){返回-1;}其他{返回-1;}}}
现在它返回:
未排序地图:
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
结果:
key/value: A/99.5 key/value: E/99.5 key/value: C/67.5 key/value: B/67.4 key/value: D/67.3
作为对《外国人》的回应(2011年11月22日):我将此解决方案用于整数Id和名称的映射,但想法是相同的,因此上面的代码可能不正确(我将在测试中编写并给您正确的代码),这是基于上面解决方案的map排序代码:
package nl.iamit.util;
import java.util.Comparator;
import java.util.Map;
public class Comparators {
public static class MapIntegerStringComparator implements Comparator {
Map<Integer, String> base;
public MapIntegerStringComparator(Map<Integer, String> base) {
this.base = base;
}
public int compare(Object a, Object b) {
int compare = ((String) base.get(a))
.compareTo((String) base.get(b));
if (compare == 0) {
return -1;
}
return compare;
}
}
}
这是测试类(我刚刚测试了它,这适用于Integer,StringMap:
package test.nl.iamit.util;
import java.util.HashMap;
import java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class TestComparators {
@Test
public void testMapIntegerStringComparator(){
HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
unSoretedMap);
TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
//the testdata:
unSoretedMap.put(new Integer(1), "E");
unSoretedMap.put(new Integer(2), "A");
unSoretedMap.put(new Integer(3), "E");
unSoretedMap.put(new Integer(4), "B");
unSoretedMap.put(new Integer(5), "F");
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
}
以下是地图比较器的代码:
public static class MapStringDoubleComparator implements Comparator {
Map<String, Double> base;
public MapStringDoubleComparator(Map<String, Double> base) {
this.base = base;
}
//note if you want decending in stead of ascending, turn around 1 and -1
public int compare(Object a, Object b) {
if ((Double) base.get(a) == (Double) base.get(b)) {
return 0;
} else if((Double) base.get(a) < (Double) base.get(b)) {
return -1;
}else{
return 1;
}
}
}
这是一个测试用例:
@Test
public void testMapStringDoubleComparator(){
HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
unSoretedMap);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
//the testdata:
unSoretedMap.put("D",new Double(67.3));
unSoretedMap.put("A",new Double(99.5));
unSoretedMap.put("B",new Double(67.4));
unSoretedMap.put("C",new Double(67.5));
unSoretedMap.put("E",new Double(99.5));
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={"D","B","C","E","A"};
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
当然,你可以让它更通用,但我只需要一个案例(地图)