问题描述
这个问题从字面上看会有点奇怪,毕竟HashMap是按哈希值存储元素的,每个元素的位置是固定的,所以无法像list一样可以通过索引值list.get(i)去获取元素,由于位置由哈希值确定,也谈不上排序。
但是,问题就在于确实会遇到一些情形,比如我定义了一个map对象
Map<Student, Integer> map = new HashMap<>();
map是由学生类作为key,整型类作为value的,我的toString函数里面,希望它能够按照Integer(实际意义为分数)的大小进行排序,并返回一个按顺序排列的字符串。
比如下面这个map
Map<Student, Integer> map = new HashMap<>();
Student a = new Student("a");
Student b = new Student("b");
Student c = new Student("c");
map.put(a, 78);
map.put(c,60);
map.put(b,99);
打印map.toString()的结果
{
[Student name:a]=78, [Student name:b]=99, [Student name:c]=60}
既不是按照插入顺序,也不是按照我们预期的value大小顺序排列。
所以说直接return map.toString()肯定是不行的,那我怎么能够构造一个字符串表示map,而且是按value大小排列的呢?
解决方案
方法① 首先网上有一些大佬的博客都介绍了用Java8的Streams进行排序,
比如:
map.entrySet().stream().sorted((e1, e2) -> e1.getValue().compareTo(e2.getValue())).forEach(System.out::println);
输完这行代码,然后直接运行,就会神奇地发现控制台打印出了按value顺序排列的map
然而,这种方法虽然很舒服,但是不能解决我的这个所面临的问题,它不能变成一个可返回的字符串,而是直接打印了出来。
方法② 仍然使用Java8的Streams进行排序,同时借助了LinkedHashMap
Map<Student, Integer> sortedMap = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors
.toMap(Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new));
这时候,我们打印一下sortedMap
String s = sortedMap.toString();
System.out.println(s);
输出结果:
这方法也很舒适,直接调用内置方法,就可以排序了,而且只需改一下参数就能按key排序或者是降序排序,十分快捷。
但是,有时候人就比较作,或者面临的情况很specific,比如说,我不希望这个返回的字符串是xx(Student) = xx(成绩)这种形式输出,但是我们知道,map这种类型的toString就是{key1=value1, key2=value2…},如果跑到人家库里面的去改写方法,这不太好…或者自己重新写一个类继承map后又重写toString,会比较麻烦。
所以就有了下面这种比较原始的方法,看上去会比之前两个多花点时间,但是结果上灵活性会更强。
方法③ 这个方法,说起来很直观,一看就懂,一般也能想到,我称之为“土办法”,也就是定义两个列表,keylist和valuelist,然后按value值对valuelist排序,交换valuelist中元素的同时交换keylist中的元素。也就是同时排序的思想。
String s = "{";
List<Student> keylist = new ArrayList<>();
List<Integer> valuelist = new ArrayList<>();
for(Map.Entry<Student, Integer> entry : map.entrySet()) {
keylist.add(entry.getKey());
valuelist.add(entry.getValue());
}//初始化keylist和valuelist
for(int i=0; i<valuelist.size(); i++) {
int minpos = i;
Integer min = valuelist.get(i);
for(int j=i+1; j<valuelist.size(); j++) {
if(valuelist.get(j).compareTo(min)<0) {
minpos = j;
min = valuelist.get(j);
}
}//选择排序(按值排序)
if(minpos!=i) {
Integer temp = valuelist.get(i);
valuelist.set(i, min);
valuelist.set(minpos, temp);//valuelist排序
Student templ = keylist.get(i);
keylist.set(i, keylist.get(minpos));
keylist.set(minpos, templ);//同时排序keylist
}
}
//个性化操作
for(int i=0; i<keylist.size(); i++) {
s += keylist.get(i)+" \t对应成绩=";
s += valuelist.get(i);
if(i < keylist.size()-1) s+=",\n";
}
s+="}";
同时排序使得将原有map分解为:两个索引值对应的list,那么我们就可以按大小顺序对list中的值操作,灵活性会比较高。
打印一下s,输出结果: