68

How to move a particular HashMap entry to the last position?

For example, I have HashMap values like this:

HashMap<String,Integer> map = new HashMap<String,Integer>();

// map = {Not-Specified 1, test 2, testtest 3};

"Not-Specified" may come in any position. it may come first or in the middle of the map. But I want to move the "Not-Specified" to the last position.

How can I do that?

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Gnaniyar Zubair
  • 8,114
  • 23
  • 61
  • 72

8 Answers8

168

To answer your question in one sentence:

Per default, Maps don't have a last entry, it's not part of their contract.


And a side note: it's good practice to code against interfaces, not the implementation classes (see Effective Java by Joshua Bloch, Chapter 8, Item 52: Refer to objects by their interfaces).

So your declaration should read:

Map<String,Integer> map = new HashMap<String,Integer>();

(All maps share a common contract, so the client need not know what kind of map it is, unless he specifies a sub interface with an extended contract).


Possible Solutions

Sorted Maps:

There is a sub interface SortedMap that extends the map interface with order-based lookup methods and it has a sub interface NavigableMap that extends it even further. The standard implementation of this interface, TreeMap, allows you to sort entries either by natural ordering (if they implement the Comparable interface) or by a supplied Comparator.

You can access the last entry through the lastEntry method:

NavigableMap<String,Integer> map = new TreeMap<String, Integer>();
// add some entries
Entry<String, Integer> lastEntry = map.lastEntry();

Linked maps:

There is also the special case of LinkedHashMap, a HashMap implementation that stores the order in which keys are inserted. There is however no interface to back up this functionality, nor is there a direct way to access the last key. You can only do it through tricks such as using a List in between:

Map<String,String> map = new LinkedHashMap<String, Integer>();
// add some entries
List<Entry<String,Integer>> entryList =
    new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
Entry<String, Integer> lastEntry =
    entryList.get(entryList.size()-1);

Proper Solution:

Since you don't control the insertion order, you should go with the NavigableMap interface, i.e. you would write a comparator that positions the Not-Specified entry last.

Here is an example:

final NavigableMap<String,Integer> map = 
        new TreeMap<String, Integer>(new Comparator<String>() {
    public int compare(final String o1, final String o2) {
        int result;
        if("Not-Specified".equals(o1)) {
            result=1;
        } else if("Not-Specified".equals(o2)) {
            result=-1;
        } else {
            result =o1.compareTo(o2);
        }
        return result;
    }

});
map.put("test", Integer.valueOf(2));
map.put("Not-Specified", Integer.valueOf(1));
map.put("testtest", Integer.valueOf(3));
final Entry<String, Integer> lastEntry = map.lastEntry();
System.out.println("Last key: "+lastEntry.getKey()
         + ", last value: "+lastEntry.getValue());

Output:

Last key: Not-Specified, last value: 1

Solution using HashMap:

If you must rely on HashMaps, there is still a solution, using a) a modified version of the above comparator, b) a List initialized with the Map's entrySet and c) the Collections.sort() helper method:

    final Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("test", Integer.valueOf(2));
    map.put("Not-Specified", Integer.valueOf(1));
    map.put("testtest", Integer.valueOf(3));

    final List<Entry<String, Integer>> entries =
        new ArrayList<Entry<String, Integer>>(map.entrySet());
    Collections.sort(entries, new Comparator<Entry<String, Integer>>(){

        public int compareKeys(final String o1, final String o2){
            int result;
            if("Not-Specified".equals(o1)){
                result = 1;
            } else if("Not-Specified".equals(o2)){
                result = -1;
            } else{
                result = o1.compareTo(o2);
            }
            return result;
        }

        @Override
        public int compare(final Entry<String, Integer> o1,
            final Entry<String, Integer> o2){
            return this.compareKeys(o1.getKey(), o2.getKey());
        }

    });

    final Entry<String, Integer> lastEntry =
        entries.get(entries.size() - 1);
    System.out.println("Last key: " + lastEntry.getKey() + ", last value: "
        + lastEntry.getValue());

}

Output:

Last key: Not-Specified, last value: 1

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Why would implementing the comparable interface and then using a TreeMap not be your first choice? You seem to know more about Maps than I do, so just wondering – nckbrz May 14 '14 at 16:03
  • @nixxbb it *is* my first choice, that's why it appears directly after the literal answer – Sean Patrick Floyd May 15 '14 at 06:55
  • oh, I see, you are talking about Comparable vs Comparator. In the OP's case, the keys are Strings, but he requires a custom order. That's why. Otherwise I'd agree with you – Sean Patrick Floyd May 15 '14 at 06:58
  • Oh, I see. Very nicely done. Thank you for the reply, I learned a lot just from your answer... – nckbrz May 17 '14 at 12:19
19

HashMap doesn't have "the last position", as it is not sorted.

You may use other Map which implements java.util.SortedMap, most popular one is TreeMap.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
卢声远 Shengyuan Lu
  • 31,208
  • 22
  • 85
  • 130
5

A SortedMap is the logical/best choice, however another option is to use a LinkedHashMap which maintains two order modes, most-recently-added goes last, and most-recently-accessed goes last. See the Javadocs for more details.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

When using numbers as the key, I suppose you could also try this:

        Map<Long, String> map = new HashMap<>();
        map.put(4L, "The First");
        map.put(6L, "The Second");
        map.put(11L, "The Last");

        long lastKey = 0;
        //you entered Map<Long, String> entry
        for (Map.Entry<Long, String> entry : map.entrySet()) {
            lastKey = entry.getKey();
        }
        System.out.println(lastKey); // 11
Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
Yoshua Nahar
  • 1,304
  • 11
  • 28
1

move does not make sense for a hashmap since its a dictionary with a hashcode for bucketing based on key and then a linked list for colliding hashcodes resolved via equals. Use a TreeMap for sorted maps and then pass in a custom comparator.

anand
  • 26
  • 1
0

In such scenario last used key is usually known so it can be used for accessing last value (inserted with the one):

class PostIndexData {
    String _office_name;
    Boolean _isGov;
    public PostIndexData(String name, Boolean gov) {
        _office_name = name;
        _isGov = gov;
    }
}
//-----------------------
class KgpData {
    String _postIndex;
    PostIndexData _postIndexData;
    public KgpData(String postIndex, PostIndexData postIndexData) {
        _postIndex = postIndex;
        _postIndexData = postIndexData;;
    }
}

public class Office2ASMPro {
    private HashMap<String,PostIndexData> _postIndexMap = new HashMap<>();
    private HashMap<String,KgpData> _kgpMap = new HashMap<>();
...
private void addOffice(String kgp, String postIndex, String officeName, Boolean gov) {
            if (_postIndexMap.get(postIndex) == null) {
                _postIndexMap.put(postIndex, new PostIndexData(officeName, gov));
            }
            _kgpMap.put( kgp, new KgpData(postIndex, _postIndexMap.get(postIndex)) );
        }
Mad Calm
  • 52
  • 4
  • Hi Mad Calm, remember to try to keep your examples concise and simple so anyone reading the question/answer can easily understand. Pasting code without much explanation usually is not a good idea :-) Thank you for your contribution. – Panthro Feb 28 '19 at 14:21
0

HashMap explicitly does not define a key order. Per the class Javadocs:

This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

A number other map types do define a key order, including LinkedHashMap and maps which implement the SortedMap interface. Java 21 is introducing a SequencedMap interface for maps such as these which have a a well-defined encounter order.

Per its Javadocs, a SequencedMap is:

A Map that has a well-defined encounter order, that supports operations at both ends, and that is reversible.

One of the operations on SequencedMap is putLast. This optional operation, for SequencedMap types that support it, inserts an entry at the end of the map, or moves it to the end if the key is already present. LinkedHashMap supports this method, but SortedMap does not. Per the metohd Javadocs:

Inserts the given mapping into the map if it is not already present, or replaces the value of a mapping if it is already present (optional operation). After this operation completes normally, the given mapping will be present in this map, and it will be the last mapping in this map's encounter order.

Scenario in question

Applying the above to your scenario in question, we get:

LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>();

populateMap(map); // Populate the map as: {Not-Specified 1, test 2, testtest 3};

map.putLast("Not-Specified", map.get("Not-Specified"));
M. Justin
  • 14,487
  • 7
  • 91
  • 130
-2
Find missing all elements from array
        int[] array = {3,5,7,8,2,1,32,5,7,9,30,5};
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for(int i=0;i<array.length;i++) {
            map.put(array[i], 1);
        }
        int maxSize = map.lastKey();
        for(int j=0;j<maxSize;j++) {
            if(null == map.get(j))
                System.out.println("Missing `enter code here`No:"+j);
        }