11

I have a HashMap<Object, Student> where the Object is the ID of the Student, and the Student is an object from Student.

How can I resort the HashMap by the Students name, student->getName()?

Chiggins
  • 8,197
  • 22
  • 56
  • 81
  • It depends what the homework "wants" you to do. Since there is no "resorting", perhaps it wants you to display/export the students (in the HashMap) in a particular order ... anyway, homework is fickle that way. –  Nov 10 '10 at 23:54
  • 1
    Dupe of http://stackoverflow.com/questions/1894081/what-is-the-easiest-way-to-sort-maps-according-to-values-in-java, http://stackoverflow.com/questions/2839003/sorting-in-hash-maps-in-java and probably many more. – BalusC Nov 11 '10 at 00:00
  • 3
    This has to be one of the top 10 Java questions. – Steve Kuo Nov 11 '10 at 01:29
  • HashMap does not provide method to sort itself. Therefore the only opton is to get a desired view and sort the view. But be aware that removing from view will also remove from HashMap itself – Volodymyr Levytskyi Aug 02 '13 at 08:17

5 Answers5

18

HashMaps are intrinsically unordered and cannot be sorted.

Instead, you can use a SortedMap implementation, such as a TreeMap.
However, even a sorted map can only sort by its keys.

If you want to sort by the values, you'll need to copy them to a sorted list.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
5

You might not be able to sort a HashMap, but you can certainly do something that provides the same effect. I was able to sort my HashMap <String, Integer> by descending value of the Integer by using the excellent code posted at the Javarevisited blog. The same principle would apply to a HashMap <String, String> object:

/*
 * Java method to sort Map in Java by value e.g. HashMap or Hashtable
 * throw NullPointerException if Map contains null values
 * It also sort values even if they are duplicates
 */
public static <K extends Comparable,V extends Comparable> Map<K,V> sortByValues(Map<K,V> map){
    List<Map.Entry<K,V>> entries = new LinkedList<Map.Entry<K,V>>(map.entrySet());

    Collections.sort(entries, new Comparator<Map.Entry<K,V>>() {

        @Override
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
            // to compare alphabetically case insensitive return this instead
            // o1.getValue().toString().compareToIgnoreCase(o2.getValue().toString()); 
        }
    });

    //LinkedHashMap will keep the keys in the order they are inserted
    //which is currently sorted on natural ordering
    Map<K,V> sortedMap = new LinkedHashMap<K,V>();

    for(Map.Entry<K,V> entry: entries){
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    return sortedMap;
}

To call this method, I use:

Map<String, Integer> sorted = sortByValues(myOriginalHashMapObject);

Read more: http://javarevisited.blogspot.com/2012/12/how-to-sort-hashmap-java-by-key-and-value.html#ixzz2akXStsGj

branchgabriel
  • 4,241
  • 4
  • 34
  • 48
Steve HHH
  • 12,947
  • 6
  • 68
  • 71
2

Maps cannot be ordered by values. You can do this, though:

Collection<Student> students = map.values();

Collection.sort(new ArrayList<Student>(students)), new Comparator<Student>() {
    public int compare(Student s1, Student s2) {
        return s1.getName().compareTo(s2.getName());
    }
});

Assuming, of course, that you need to iterate over the values. (Why else would you want it ordered like that?)

Good luck.

Todd
  • 3,438
  • 1
  • 27
  • 36
  • TreeMaps are ordered. Saying that Maps in general cannot be is not completely accurate. Yes, I know the OP said to use a HashMap, but you said Map, not HashMap. –  Nov 11 '10 at 00:23
  • 1
    Ordered by keys. I said ordered by values, not keys. – Todd Nov 11 '10 at 00:27
0

I would definitely use a New Class that will store the key and the Object.

Then you can put every element of the Map into an ArrayList in the form of this class, and finally use a comparator to sort the ArrayList, afterwards you simply build a new Map. Code will be something like this:

Map<Object, Student> valueMap = new LinkedHashMap<String, String>();
List<Student> pairValueList = new ArrayList<PairValue>();

PairValue p;
for (Map.Entry<Object, Student> entry : map.entrySet()) {
  Object key = entry.getKey();
  Student value = entry.getValue();        
  p = new PairValue(key, value);
  pairValueList.add(p);
 }

Collections.sort(pairValueList, new Comparator<PairValue>() {
  @Override
  public int compare(PairValue c1, PairValue c2) {
    return c1.getLabel().compareTo(c2.getLabel());
  }
});

for (PairValue pv : pairValueList) {
  valueMap.put(pv.getValue(), pv.getStudent());
}

The PairValue class

    class PairValue {    

  private Object value;    
  private Student student;

  public PairValue(Object value, String student) {
    this.value = value;
    this.student= student;
  }

  public String getValue() {
    return value;
  }

  public String getStudent() {
    return student;
  }    
}

Thats the way I solved some similar problem I had in the past. Please Note that the returned map implementation needs to be a LinkedHashMap.

will824
  • 2,203
  • 4
  • 27
  • 29
0

HashMaps cannot be sorted by their values. A Map is designed for constant time lookups based on the key, so ordering by values should not be necessary. If you need to sort by name, I suggest using a SortedSet and creating a comparator that sorts by the names.

class StudentComparator implements Comparator<Student> {
    int compare(Student s1, Student s2) {
       return s1.getName().compareTo(s2.getName());
    }
}

If you need both a constant time lookup and a sorted-by-value set, then you may need to maintain a map and a set.

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406