如何对HashMap进行排序

问题描述

这个问题从字面上看会有点奇怪,毕竟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,输出结果:
在这里插入图片描述

参考链接

如何对HashMap进行排序


版权声明:本文为weixin_45577864原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。