我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
我对Java比较陌生,经常发现需要对值进行Map<Key,Value>排序。
由于这些值不是唯一的,我发现自己将keySet转换为一个数组,并使用自定义比较器通过数组排序对该数组进行排序,该比较器根据与该键关联的值进行排序。
有没有更简单的方法?
当前回答
主要问题。如果您使用第一个答案(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
}
其他回答
用Java中最简单的方式对任何Hashmap进行排序。我们不需要将其存储在树图、列表等中。
在这里,我将使用Java Streams:
让我们按其值(升序)对该地图进行排序
Map<String, Integer> mp= new HashMap<>();
mp.put("zebra", 1);
mp.put("blossom", 2);
mp.put("gemini", 3);
mp.put("opera", 7);
mp.put("adelaide", 10);
Map<String, Integer> resultMap= mp.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1, LinkedHashMap::new));
现在,您可以通过多种方式打印排序后的resultMap,例如使用高级for循环或迭代器。
上面的映射也可以按值的降序排序
Map<String, Integer> resultMap= mp.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1, LinkedHashMap::new));
现在让我们来看另一个场景,我们将“User”存储在地图中,并根据“User”的“name”按升序(词典)对其进行排序:
User u1= new User("hi", 135);
User u2= new User("bismuth", 900);
User u3= new User("alloy", 675);
User u4= new User("jupiter", 342);
User u5= new User("lily", 941);
Map<String, User> map2= new HashMap<>();
map2.put("zebra", u3);
map2.put("blossom", u5);
map2.put("gemini", u1);
map2.put("opera", u2);
map2.put("adelaide", u4);
Map<String, User> resultMap=
map2.entrySet().stream().sorted(Map.Entry.<String, User>comparingByValue( (User o1, User o2)-> o1.getName().compareTo(o2.getName()))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e2, LinkedHashMap::new));
class User
{
String name;
int id;
public User(String name, int id) {
super();
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User [name=" + name + ", id=" + id + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
因为地图是无序的要对其进行排序,我们可以执行以下操作
Map<String, String> map= new TreeMap<String, String>(unsortMap);
您应该注意,与哈希映射不同,树映射保证其元素将按升序键排序。
如果倾向于使用一个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中的新功能实现这一点,请执行以下操作:
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());
}
条目使用给定的比较器按其值排序。或者,如果您的值可以相互比较,则不需要显式比较器:
<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}
返回的列表是调用此方法时给定映射的快照,因此两者都不会反映对另一个的后续更改。对于地图的实时可迭代视图:
<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}
返回的可迭代对象在每次迭代时都会创建给定映射的新快照,因此除非并发修改,否则它将始终反映映射的当前状态。
Geeks For Geeks对HashMap按值排序
Input : Key = Math, Value = 98
Key = Data Structure, Value = 85
Key = Database, Value = 91
Key = Java, Value = 95
Key = Operating System, Value = 79
Key = Networking, Value = 80
Output : Key = Operating System, Value = 79
Key = Networking, Value = 80
Key = Data Structure, Value = 85
Key = Database, Value = 91
Key = Java, Value = 95
Key = Math, Value = 98
Solution: The idea is to store the entry set in a list and sort the list on the basis of values. Then fetch values and keys from the list and put them in a new hashmap. Thus, a new hashmap is sorted according to values.
Below is the implementation of the above idea:
// Java program to sort hashmap by values
import java.util.*;
import java.lang.*;
public class GFG {
// function to sort hashmap by values
public static HashMap<String, Integer> sortByValue(HashMap<String, Integer> hm)
{
// Create a list from elements of HashMap
List<Map.Entry<String, Integer> > list =
new LinkedList<Map.Entry<String, Integer> >(hm.entrySet());
// Sort the list
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());
}
});
// put data from sorted list to hashmap
HashMap<String, Integer> temp = new LinkedHashMap<String, Integer>();
for (Map.Entry<String, Integer> aa : list) {
temp.put(aa.getKey(), aa.getValue());
}
return temp;
}
// Driver Code
public static void main(String[] args)
{
HashMap<String, Integer> hm = new HashMap<String, Integer>();
// enter data into hashmap
hm.put("Math", 98);
hm.put("Data Structure", 85);
hm.put("Database", 91);
hm.put("Java", 95);
hm.put("Operating System", 79);
hm.put("Networking", 80);
Map<String, Integer> hm1 = sortByValue(hm);
// print the sorted hashmap
for (Map.Entry<String, Integer> en : hm1.entrySet()) {
System.out.println("Key = " + en.getKey() +
", Value = " + en.getValue());
}
}
}
Output
Key = Operating System, Value = 79
Key = Networking, Value = 80
Key = Data Structure, Value = 85
Key = Database, Value = 91
Key = Java, Value = 95
Key = Math, Value = 98