Abe*_*Abe 1569 java sorting collections dictionary
我是Java的新手,经常发现我需要对Map<Key, Value>
值进行排序.
由于值不是唯一的,我发现自己将其转换keySet
为a array
,并通过数组排序对该数组进行排序,并使用自定义比较器对与键关联的值进行排序.
有没有更简单的方法?
Car*_*age 869
这是一个通用友好版本:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 410
此代码可以以多种方式中断.如果您打算使用提供的代码,请务必阅读注释以了解其含义.例如,不能再通过其密钥检索值.(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
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
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}
Run Code Online (Sandbox Code Playgroud)
Bri*_*etz 313
Java 8提供了一个新的答案:将条目转换为流,并使用Map.Entry中的比较器组合器:
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue());
Run Code Online (Sandbox Code Playgroud)
这将允许您使用按值的升序排序的条目.如果要降序值,只需反转比较器:
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
Run Code Online (Sandbox Code Playgroud)
如果值不具有可比性,则可以传递显式比较器:
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(comparator));
Run Code Online (Sandbox Code Playgroud)
然后,您可以继续使用其他流操作来使用数据.例如,如果您想要新地图中的前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));
Run Code Online (Sandbox Code Playgroud)
或打印到System.out
:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
Ste*_*hen 210
三个1行答案......
我会使用Google Collections Guava来执行此操作 - 如果您的值是Comparable
可以使用的话
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Run Code Online (Sandbox Code Playgroud)
这将为地图创建一个函数(对象)[将任何键作为输入,返回相应的值],然后对它们[值]应用自然(可比较)排序.
如果他们没有可比性,那么你需要做一些事情
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Run Code Online (Sandbox Code Playgroud)
这些可以应用于TreeMap(作为Ordering
extends Comparator
),或者在某些排序后应用于LinkedHashMap
注意:如果您打算使用TreeMap,请记住,如果比较== 0,那么该项目已经在列表中(如果您有多个比较相同的值,则会发生这种情况).为了缓解这个问题,您可以将密钥添加到比较器中(假设您的密钥和值是Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
Run Code Online (Sandbox Code Playgroud)
= 对键映射的值应用自然排序,并使用键的自然顺序对其进行复合
请注意,如果您的密钥与0比较,这仍然不起作用,但这应该足以满足大多数comparable
项目(因为hashCode
,equals
并且compareTo
通常是同步的...)
请参阅Ordering.onResultOf()和Functions.forMap().
所以现在我们已经有了一个可以满足我们想要的比较器,我们需要从中获得结果.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Run Code Online (Sandbox Code Playgroud)
现在这很可能会起作用,但是:
TreeMap
; 没有必要尝试比较插入的键,直到它没有值,直到放置之后,即,它会非常快地中断第1点对我来说是一个破坏性的事情; 谷歌收藏是非常懒惰(这是好的:你可以在瞬间完成几乎所有的操作;真正的工作是在你开始使用结果时完成的),这需要复制整个地图!
不过不用担心; 如果你对以这种方式排序的"实时"地图足够痴迷,你可以解决上述问题中的一个而不是两个(!),如下所示:
注意:这在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"));
}
}
Run Code Online (Sandbox Code Playgroud)
当我们放置时,我们确保哈希映射具有比较器的值,然后放入TreeSet进行排序.但在此之前,我们检查哈希映射以查看密钥实际上并不重复.此外,我们创建的比较器还将包含密钥,以便重复值不会删除非重复键(由于==比较).这两项对于确保保留地图合同至关重要 ; 如果你认为你不想那样,那么你几乎就是完全颠倒了地图(to Map<V,K>
).
需要将构造函数调用为
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
Run Code Online (Sandbox Code Playgroud)
dev*_*ore 185
来自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;
}
Run Code Online (Sandbox Code Playgroud)
ass*_*ias 62
使用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));
Run Code Online (Sandbox Code Playgroud)
vol*_*ley 31
对键进行排序需要比较器查找每个比较的每个值.一个更具可伸缩性的解决方案将直接使用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());
}
}
Run Code Online (Sandbox Code Playgroud)
有一些方法可以减少上述解决方案的内存轮换.例如,创建的第一个ArrayList可以重新用作返回值; 这需要抑制一些泛型警告,但对于可重用的库代码可能是值得的.此外,不必在每次调用时重新分配比较器.
这是一个更有效但尽管不那么有吸引力的版本:
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();
Run Code Online (Sandbox Code Playgroud)
最后,如果您需要不断访问已排序的信息(而不是仅仅偶尔对其进行排序),则可以使用其他多地图.如果您需要更多详细信息,请告诉我们......
p3t*_*t0r 26
commons-collections库包含一个名为TreeBidiMap的解决方案.或者,您可以查看Google Collections API.它有你可以使用的TreeMultimap.
如果你不想使用这些框架......它们带有源代码.
Ant*_*ony 25
我已经查看了给定的答案,但是当很多键具有相同的值时,其中很多都比需要的更复杂或者删除了地图元素.
这是一个我觉得更合适的解决方案:
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,地图从最高值到最低值排序.
gde*_*ohn 19
要使用Java 8中的新功能实现此目的:
import static java.util.Map.Entry.comparingByValue;
import static java.util.stream.Collectors.toList;
<K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) {
return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList());
}
Run Code Online (Sandbox Code Playgroud)
条目按照给定的比较器的值排序.或者,如果您的值可以相互比较,则不需要显式比较器:
<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}
Run Code Online (Sandbox Code Playgroud)
返回的列表是调用此方法时给定映射的快照,因此两者都不会反映对另一个的后续更改.对于地图的实时可迭代视图:
<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}
Run Code Online (Sandbox Code Playgroud)
返回的iterable在每次迭代时都会创建给定映射的新快照,因此除非进行并发修改,否则它将始终反映映射的当前状态.
Arp*_*ini 18
给定地图
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);
Run Code Online (Sandbox Code Playgroud)
根据值按升序对地图进行排序
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);
Run Code Online (Sandbox Code Playgroud)
根据值按降序对地图进行排序
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);
Run Code Online (Sandbox Code Playgroud)
输出:
{软件=50,技术=70,美国=100,工作=200,机会=200}
{工作=200,机会=200,美国=100,技术=70,软件=50}
Suj*_*y A 16
创建自定义比较器并在创建新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));
}
}
Run Code Online (Sandbox Code Playgroud)
在主函数中使用以下代码
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);
Run Code Online (Sandbox Code Playgroud)
输出:
{B=75, D=50, C=50, A=35}
Run Code Online (Sandbox Code Playgroud)
Lyu*_*mil 14
虽然我同意对地图进行排序的不断需要可能是一种气味,但我认为以下代码是最简单的方法,而不使用不同的数据结构.
public class MapUtilities {
public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
Collections.sort(entries, new ByValue<K, V>());
return entries;
}
private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
Run Code Online (Sandbox Code Playgroud)
}
这是一个令人尴尬的不完整的单元测试:
public class MapUtilitiesTest extends TestCase {
public void testSorting() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("One", 1);
map.put("Two", 2);
map.put("Three", 3);
List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
assertEquals("First", "One", sorted.get(0).getKey());
assertEquals("Second", "Two", sorted.get(1).getKey());
assertEquals("Third", "Three", sorted.get(2).getKey());
}
Run Code Online (Sandbox Code Playgroud)
}
结果是Map.Entry对象的排序列表,您可以从中获取键和值.
小智 12
使用通用比较器,例如:
final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {
private Map<K,V> map;
private MapValueComparator() {
super();
}
public MapValueComparator(Map<K,V> map) {
this();
this.map = map;
}
public int compare(K o1, K o2) {
return map.get(o1).compareTo(map.get(o2));
}
}
Run Code Online (Sandbox Code Playgroud)
mic*_*mit 11
当你有两个相等的项目时,最多投票的答案不起作用.TreeMap保留相同的值.
exmaple:未分类的地图
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.
在示例中:
class ValueComparator实现Comparator {
地图基地; public ValueComparator(Map base){this.base = base; }
public int compare(Object a,Object b){
Run Code Online (Sandbox Code Playgroud)if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return -1; } else { return -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
作为对Aliens的回应(2011年11月22日):我使用这个解决方案来获取Integer 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是测试类(我刚测试过,这适用于Integer,String Map:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是地图比较器的代码:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是对此的测试用例:
@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);
}
Run Code Online (Sandbox Code Playgroud)
cource你可以使它更通用,但我只需要1个案例(地图)
而不是Collections.sort
像我建议的那样使用Arrays.sort
.实际上Collections.sort
是这样的:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
Run Code Online (Sandbox Code Playgroud)
它只是调用toArray
列表然后使用Arrays.sort
.这样,所有映射条目将被复制三次:一次从映射到临时列表(无论是LinkedList还是ArrayList),然后到临时数组,最后到新映射.
我的解决方案省略了这一步,因为它不会创建不必要的LinkedList.这是代码,通用友好和性能最佳:
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map)
{
@SuppressWarnings("unchecked")
Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);
Arrays.sort(array, new Comparator<Map.Entry<K, V>>()
{
public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2)
{
return e1.getValue().compareTo(e2.getValue());
}
});
Map<K, V> result = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : array)
result.put(entry.getKey(), entry.getValue());
return result;
}
Run Code Online (Sandbox Code Playgroud)
这是Anthony的答案的变体,如果存在重复值,则不起作用:
public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
final V v1 = map.get(k1);
final V v2 = map.get(k2);
/* Not sure how to handle nulls ... */
if (v1 == null) {
return (v2 == null) ? 0 : 1;
}
int compare = v2.compareTo(v1);
if (compare != 0)
{
return compare;
}
else
{
Integer h1 = k1.hashCode();
Integer h2 = k2.hashCode();
return h2.compareTo(h1);
}
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
Run Code Online (Sandbox Code Playgroud)
请注意,它是如何处理空值的.
这种方法的一个重要优点是它实际上返回了一个Map,与此处提供的其他一些解决方案不同.
最佳方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class OrderByValue {
public static void main(String a[]){
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("java", 20);
map.put("C++", 45);
map.put("Unix", 67);
map.put("MAC", 26);
map.put("Why this kolavari", 93);
Set<Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
{
public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
{
return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
//return (o2.getValue()).compareTo( o1.getValue() );//Descending order
}
} );
for(Map.Entry<String, Integer> entry:list){
System.out.println(entry.getKey()+" ==== "+entry.getValue());
}
}}
Run Code Online (Sandbox Code Playgroud)
产量
java ==== 20
MAC ==== 26
C++ ==== 45
Unix ==== 67
Why this kolavari ==== 93
Run Code Online (Sandbox Code Playgroud)
小智 7
主要问题.如果您使用第一个答案(Google将您带到此处),请更改比较器以添加相等的子句,否则您无法通过键从sorted_map获取值:
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return 1;
} else if (base.get(a) < base.get(b)){
return -1;
}
return 0;
// returning 0 would merge keys
}
Run Code Online (Sandbox Code Playgroud)
这个问题已经有很多答案了,但没有一个提供我正在寻找的东西,一个地图实现返回按关联值排序的键和条目,并将此属性维护为键,并在地图中修改值.两个其他 问题询问此特别.
我编写了一个通用的友好示例来解决这个用例.此实现不遵守Map接口的所有合同,例如反映从原始对象中的keySet()和entrySet()返回的集合中的值更改和删除.我觉得这样的解决方案太大而无法包含在Stack Overflow答案中.如果我设法创建一个更完整的实现,也许我会将它发布到Github然后链接到这个答案的更新版本.
import java.util.*;
/**
* A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
* by associated values based on the the comparator provided at construction
* time. The order of two or more keys with identical values is not defined.
* <p>
* Several contracts of the Map interface are not satisfied by this minimal
* implementation.
*/
public class ValueSortedMap<K, V> extends HashMap<K, V> {
protected Map<V, Collection<K>> valueToKeysMap;
// uses natural order of value object, if any
public ValueSortedMap() {
this((Comparator<? super V>) null);
}
public ValueSortedMap(Comparator<? super V> valueComparator) {
this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
}
public boolean containsValue(Object o) {
return valueToKeysMap.containsKey(o);
}
public V put(K k, V v) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
valueToKeysMap.get(oldV).remove(k);
}
super.put(k, v);
if (!valueToKeysMap.containsKey(v)) {
Collection<K> keys = new ArrayList<K>();
keys.add(k);
valueToKeysMap.put(v, keys);
} else {
valueToKeysMap.get(v).add(k);
}
return oldV;
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public V remove(Object k) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
super.remove(k);
valueToKeysMap.get(oldV).remove(k);
}
return oldV;
}
public void clear() {
super.clear();
valueToKeysMap.clear();
}
public Set<K> keySet() {
LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
for (V v : valueToKeysMap.keySet()) {
Collection<K> keys = valueToKeysMap.get(v);
ret.addAll(keys);
}
return ret;
}
public Set<Map.Entry<K, V>> entrySet() {
LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
for (Collection<K> keys : valueToKeysMap.values()) {
for (final K k : keys) {
final V v = get(k);
ret.add(new Map.Entry<K,V>() {
public K getKey() {
return k;
}
public V getValue() {
return v;
}
public V setValue(V v) {
throw new UnsupportedOperationException();
}
});
}
}
return ret;
}
}
Run Code Online (Sandbox Code Playgroud)
根据上下文,使用java.util.LinkedHashMap<T>
哪个记住项目放入地图的顺序.否则,如果您需要根据其自然顺序对值进行排序,我建议您维护一个可以进行排序的单独List Collections.sort()
.
小智 5
这太复杂了.地图不应该按照Value对它们进行排序.最简单的方法是创建自己的类,以满足您的要求.
在示例中,您应该在*所在的位置添加TreeMap比较器.但是通过java API,它只为比较器提供键,而不是值.此处所述的所有示例均基于2个地图.一个哈希和一个新树.这很奇怪.
这个例子:
Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);
Run Code Online (Sandbox Code Playgroud)
因此,将地图更改为一组:
ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);
Run Code Online (Sandbox Code Playgroud)
你将创建类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;
}
}
Run Code Online (Sandbox Code Playgroud)
和比较者类:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这样您就可以轻松添加更多依赖项.
最后一点我将添加简单的迭代器:
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()
);
}
Run Code Online (Sandbox Code Playgroud)
Afaik 最干净的方法是利用集合对值进行排序:
Map<String, Long> map = new HashMap<String, Long>();
// populate with data to sort on Value
// use datastructure designed for sorting
Queue queue = new PriorityQueue( map.size(), new MapComparable() );
queue.addAll( map.entrySet() );
// get a sorted map
LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>();
for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) {
linkedMap.put(entry.getKey(), entry.getValue());
}
public static class MapComparable implements Comparator<Map.Entry<String, Long>>{
public int compare(Entry<String, Long> e1, Entry<String, Long> e2) {
return e1.getValue().compareTo(e2.getValue());
}
}
Run Code Online (Sandbox Code Playgroud)
由于TreeMap <>不适用于可以相等的值,我使用了这个:
private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
return list;
}
Run Code Online (Sandbox Code Playgroud)
您可能希望将列表放在LinkedHashMap中,但如果您只是立即迭代它,这是多余的......
迟到。
随着Java-8的出现,我们可以以非常简单/简洁的方式将流用于数据处理。您可以使用流按值对映射条目进行排序,并创建一个LinkedHashMap来保留插入顺序的迭代。
例如:
LinkedHashMap sortedByValueMap = map.entrySet().stream()
.sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)) //first sorting by Value, then sorting by Key(entries with same value)
.collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);
Run Code Online (Sandbox Code Playgroud)
对于逆序订购,请更换:
comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
Run Code Online (Sandbox Code Playgroud)
与
comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()
Run Code Online (Sandbox Code Playgroud)
小智 5
在 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());
}
Run Code Online (Sandbox Code Playgroud)
如果您使用的是 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());
}
Run Code Online (Sandbox Code Playgroud)
小智 5
使用 java 8 可以很容易地实现这一点
public static LinkedHashMap<Integer, String> sortByValue(HashMap<Integer, String> map) {
List<Map.Entry<Integer, String>> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.comparingByValue());
LinkedHashMap<Integer, String> sortedMap = new LinkedHashMap<>();
list.forEach(e -> sortedMap.put(e.getKey(), e.getValue()));
return sortedMap;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1339360 次 |
最近记录: |