原文:《关于HashMap排序的一点浅见》 作者:mgoann
我们在对hashmap进行排序的时候存在一定的误解,首先在这更正下~
我在遇到这个问题的时候也上网查了相关的资料,发现大家在进行hashmap进行排序的时候存在一定的了解误区。
有很多人认为把hashmap遍历一下,然后对其中的key和value进行排序,然后打印出来~
大家想一想,这样的hashmap排序根本就不是对hashmap进行排序~最多也只能称为hashmap遍历,hashmap的遍历问题很简单,网上也有很多相关的资料,下面我给出一个例程:
- public static void itHashMap(Map<Object, Object> m) {
- Set set = m.entrySet();
- Iterator it = set.iterator();
- while(it.hasNext()) {
- Map.Entry<Object, Object> me = (Map.Entry<Object, Object>)it.next();
- System.out.println("key:" + me.getKey() + "----value" + me.getValue());
- }
- }
这个方法可以将hashmap遍历,并打印出相应的key和value!
其实,单纯的hashmap是无法实现排序的,我这里的排序是指,我们将键值对按照一定的顺序put进hashmap里,然后在进行取键值对的操作的时候,在按照put进去的顺序把键值对取出来。
下面我来浅显的解释下为什么不能排序.
- HashMap<String, Date> hm = new HashMap<String, Date>();
- hm.put("1", new Date(8,10,1));
- hm.put("3", new Date(8,10,3));
- hm.put("2", new Date(8,10,2));
- hm.put("5", new Date(8,10,5));
- hm.put("9", new Date(8,10,9));
- hm.put("8", new Date(8,10,8));
- hm.put("6", new Date(8,10,6));
- hm.put("7", new Date(8,10,7));
我在hashmap里按照以上的顺序放入9个键值对,然后我遍历hashmap打印出上面hm里的键值对:
key:3----value:Tue Nov 03 00:00:00 CST 1908
key:5----value:Thu Nov 05 00:00:00 CST 1908
key:7----value:Sat Nov 07 00:00:00 CST 1908
key:2----value:Mon Nov 02 00:00:00 CST 1908
key:9----value:Mon Nov 09 00:00:00 CST 1908
key:8----value:Sun Nov 08 00:00:00 CST 1908
key:6----value:Fri Nov 06 00:00:00 CST 1908
key:1----value:Sun Nov 01 00:00:00 CST 1908
我们从上面的结果可以看出,取出来的顺序并我们想要的顺序!
hashmap在put的时候是根据key的hashcode进行hash然后放入对应的地方!通过Debug可以看到HashMap的存放位置,网上有很多人说是随即存放,随即取,其实不然,放的时候是根据hashcode经过hash算法进行存放的,而我们取的时候也是想过key来取value。所以你取出来排完序,在放入HashMap的时候,它又把顺序打乱了~~(这里说的打乱也不是完全没有规律可循的,主要是你根据你的key来放的)!
所以我们要实现HashMap排序的时候就要借用别的集合来完成这个功能!
基本的思路是这样的,首先遍历HashMap取出相应的键值对,然后根据key或者是value进行排序,然后放入一个有序的集合当中,这样就实现了排序!
java在JDK1.4以后提供了LinkedHashMap来帮我们实现了有序的HashMap!
LinkedHashMap取键值对的时,是按照你放入的顺序来取的!
具体的排序算法有很多种~也可以自己写个冒泡排序取排列~~
再排完序以后,我们要找到key对应的vluae或是value对应的key!找key对应的vluea的时候很简单,网上有很多资料,我这里就不说了~~关键说说找value对应的key的时候,有的朋友就不知道该怎么做了~~
下面我提供两种思路
一种是我们可以把value放入一个数组,key也放入一个数组,这样在排序的时候按照value排序的时候,我们把key的位置也交换一次,这样就保证了键值对的完整性,见例程:
- private static void sortHashMap(HashMap<String, Date> hm) {
- List<Date> dateListSorted = new ArrayList<Date> ();
- int size = hm.size();
- Date[] dates = new Date[size];
- Set set = hm.keySet();
- Iterator iteratorSet = set.iterator();
- int k = 0;
- while(iteratorSet.hasNext()) {
- String keyStr = (String)iteratorSet.next();
- System.out.println("key from set----" + keyStr);
- if(keyStr!=null && !(keyStr.length()<1)) {
- dates[k] = hm.get(keyStr);
- k++;
- }
- System.out.println("value from hashmap----" + hm.get(keyStr));
- }
- iteratorList.next());
- int sizeList = dates.length;
- Object[] keyObj = set.toArray();
- for(int i=0;i<sizeList;i++) {//冒泡排序
- for(int j=i;j<sizeList;j++) {
- if(dates[j].after(dates[i])) {
- Date temp = dates[i];//交换value
- dates[i] = dates[j];
- dates[j] = temp;
- Object objectTemp = keyObj[i];//交换key
- keyObj[i] = keyObj[j];
- keyObj[j] = objectTemp;
- }
- }
- }
- for(int i=0;i<keyObj.length;i++) {
- System.out.println("key from array sorted----" + keyObj[i]);
- }
- for(int i=0;i<dates.length;i++) {
- System.out.println("value from list sorted----" + dates[i]);
- }
- LinkedHashMap lhm = new LinkedHashMap();
- for(int i=0;i<keyObj.length;i++) {//把经过排序的键值对放入Linked中
- lhm.put(keyObj[i], dates[i]);
- }
- /*
- *遍历Linkedhashmap打印出结果
- */
- Set set1 = lhm.entrySet();
- Iterator it = set1.iterator();
- while(it.hasNext()) {
- Map.Entry me = (Map.Entry)it.next();
- System.out.println("key:" + me.getKey() + "----value" + me.getValue());
- }
- }
这种方法比较直观,可是效率比较低,而且对内存也很浪费,你想想,在这个过程中我们new了两个长度是hm.size()长度的数组,用于存放key和value,排序也是我们自己写的冒泡排序法~~
下面我介绍第二种方法:
第二种方法的思路是这样的,我们用hm.value()放回hm的vuale集合,再把它转换成一个数组,然后调用jdk给我们提供的Arrays.sort()方法去帮我们排序,这样效率会比自己写的冒泡排序法效率高的多!
在调用sort方法的时候我们需要写一个比较器,比较器的代码如下:
- package com.mgoann.test1;
- import java.util.Comparator;
- import java.util.Date;
- public final class DateComp implements Comparator {
- public int compare(Object o1, Object o2) {
- Date date1 = (Date)o1;
- Date date2 = (Date)o2;
- return date2.compareTo(date1);
- }
- }
剩下的就是更具value去找相应的key的问题了~~这里我用了一个for循环和Iterator去查找hm里的key,找到以后我们就放到LinkedHashMap里~,详见例程:
- package com.mgoann.test1;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Comparator;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.Set;
- public class Sort {
- /**
- * @param args
- */
- public static void main(String[] args) {
- HashMap<String, Date> hm = new HashMap<String, Date>();
- LinkedHashMap<String, Date> linkedHM = new LinkedHashMap<String, Date>();
- hm.put("1", new Date(8,10,1));
- hm.put("3", new Date(8,10,3));
- hm.put("2", new Date(8,10,2));
- hm.put("5", new Date(8,10,5));
- hm.put("9", new Date(8,10,9));
- hm.put("8", new Date(8,10,8));
- hm.put("6", new Date(8,10,6));
- hm.put("7", new Date(8,10,7));
- itHashMap(hm);
- linkedHM = sortHashMap(hm);
- itHashMap(linkedHM);
- }
- private static LinkedHashMap sortHashMap(HashMap<String, Date> hm) {
- LinkedHashMap<String, Date> linkedHM = new LinkedHashMap<String, Date>();
- final Comparator DATE_COMP = new DateComp();
- int index = 0;
- Collection<Date> DateColl = hm.values();
- Object[] dates = DateColl.toArray();
- Arrays.sort(dates, DATE_COMP);
- System.out.println("dates after sorted");
- printElements(dates);
- Set set = hm.entrySet();
- for(int j=0;j<dates.length;j++) {
- Iterator it = set.iterator();
- while(it.hasNext()) {
- Map.Entry<String, Date> me = (Map.Entry<String, Date>)it.next();
- if(dates[j].equals(me.getValue())) {
- linkedHM.put(me.getKey(), me.getValue());
- }
- }
- }
- return linkedHM;
- }
- public static void printElements(Collection c) {
- Iterator it = c.iterator();
- while(it.hasNext()) {
- System.out.println(it.next());
- }
- }
- public static void printElements(Object[] dates) {
- for(int i=0;i<dates.length;i++) {
- System.out.println(dates[i]);
- }
- }
- public static void itHashMap(Map<String, Date> m) {
- Set set = m.entrySet();
- Iterator it = set.iterator();
- while(it.hasNext()) {
- Map.Entry<String, Date> me = (Map.Entry<String, Date>)it.next();
- System.out.println("key:" + me.getKey() + "----value:" + me.getValue());
- }
- }
- }
这样我们就实现了HashMap的排序问题!
程序运行结果如下:
key:3----value:Tue Nov 03 00:00:00 CST 1908
key:5----value:Thu Nov 05 00:00:00 CST 1908
key:7----value:Sat Nov 07 00:00:00 CST 1908
key:2----value:Mon Nov 02 00:00:00 CST 1908
key:9----value:Mon Nov 09 00:00:00 CST 1908
key:8----value:Sun Nov 08 00:00:00 CST 1908
key:6----value:Fri Nov 06 00:00:00 CST 1908
key:1----value:Sun Nov 01 00:00:00 CST 1908
dates after sorted
Mon Nov 09 00:00:00 CST 1908
Sun Nov 08 00:00:00 CST 1908
Sat Nov 07 00:00:00 CST 1908
Fri Nov 06 00:00:00 CST 1908
Thu Nov 05 00:00:00 CST 1908
Tue Nov 03 00:00:00 CST 1908
Mon Nov 02 00:00:00 CST 1908
Sun Nov 01 00:00:00 CST 1908
key:9----value:Mon Nov 09 00:00:00 CST 1908
key:8----value:Sun Nov 08 00:00:00 CST 1908
key:7----value:Sat Nov 07 00:00:00 CST 1908
key:6----value:Fri Nov 06 00:00:00 CST 1908
key:5----value:Thu Nov 05 00:00:00 CST 1908
key:3----value:Tue Nov 03 00:00:00 CST 1908
key:2----value:Mon Nov 02 00:00:00 CST 1908
key:1----value:Sun Nov 01 00:00:00 CST 1908
有人质疑:
第二种方法,根据value去找相应的key的问题:如果两个key对应相同的Value,你的算法是否就无效了!?
把比较器修改成Map.Entry<String, Date>对象的比较,然后直接用Array.sort(Map.Entry<String, Date>[]),不是更方便?