C# refugee seeks a bit of Java collections help - java

I need to store key/value info in some type of collection. In C#, I'd define a dictionary like this:
var entries = new Dictionary<string, int>();
entries.Add("Stop me", 11);
entries.Add("Feed me", 12);
entries.Add("Walk me", 13);
Then I would access the values so:
int value = entries["Stop me"];
How do I do this in Java? I've seen examples with ArrayList, but I'd like the solution with generics, if possible.

You want to use a Map
Map<String, Integer> m = new HashMap<String, Integer>();
m.put("Stop me", 11);
Integer i = m.get("Stop me"); // i == 11
Note that on the last line, I could have said:
int i = m.get("Stop me");
Which is shorthand for (with Java's auto-unboxing):
int i = m.get("Stop me").intValue()
If there is no value in the map at the given key, the get returns null and this expression throws a NullPointerException. Hence it's always a good idea to use the boxed type Integer in this case

Use a java.util.Map. There are several implementations:
HashMap: O(1) lookup, does not maintain order of keys
TreeMap: O(log n) lookup, maintains order of keys, so you can iterate over them in a guaranteed order
LinkedHashMap: O(1) lookup, iterates over keys in the order they were added to the map.
You use them like:
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Stop me", 11);
map.put("Feed me", 12);
int value = map.get("Stop me");
For added convenience working with collections, have a look at the Google Collections library. It's excellent.

You use a Map in Java.
Note that you can't use int (or any other primitive type) as a generic type parameter, but because of autoboxing, it still behaves almost as if it were a Map<String, int> instead of a Map<String, Integer>. (You don't want to be doing a lot of autoboxing in performance-sensitive code, though.)
Map<String, Integer> entries = new HashMap<String, Integer>();
entries.put("Stop me", 11);
entries.put("Feed me", 12);
entries.put("Walk me", 13);
int value = entries.get("Stop me"); // if you know it exists
// If you're not sure whether the map contains a value, it's better to do:
Integer boxedValue = entries.get("Punch me");
if (boxedValue != null) {
int unboxedValue = boxedValue;
...
}

It looks like you are looking for something like HashMap

Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Stop Me", 11);
map.put("Feed Me", 12);
map.put("Walk Me", 13);
Integer x; // little hack
int value = (x = a.get("aaa")) == null? 0 : x;
as alternative you can try Enum:
enum Action {
STOP(11),
FEED(12),
WALK(13);
private final int value;
private Action(int value) {
this.value = value;
}
public int value() {
return value;
}
public static Action valueOf(int value) {
for (Action action : values()) {
if (action.value == value) {
return action;
}
}
return null; // or a null-object
}
}
test:
public void action() {
Action action = Action.valueOf("FEED");
// or Action.FEED for more compile-time safety
int value = action.value();
// instantiating by code
Action walk = Action.valueOf(13);
}

You definitely want a HashMap, which is the Java version of C# Dictionary.

Related

How can I make a Map with two indexes?

I have one Map in java like this:
Map<String index1, Map<String index 2, Object obj>> map = new HashMap<>();
I want to get my Object in the map by using index1 and index2 as lookups.
The easiest way to do this would be to use Guava's Table, if you're willing to use a third party library.
It works like this:
Table<String, String, Object> table = HashBasedTable.create();
table.put(index1, index2, obj);
Object retrievedObject = table.get(index1, index2);
You can add it to your project by following these instructions: How to add Guava to Eclipse project
If you don't want to use Guava, you have a big problem. If you try to insert an element with new first key, you have to make sure the innermap already exists. This means, every time you do put, you have to retrieve the innerMap, see if it exists, and then create it if it does not. You will have to do this every time you call Map.put. Also, you risk throwing a NullPointerException if the inner map doesn't exist when you call get on the inner map.
If you do this, should wrap your Map<String, Map<String, Object> in an outer class to manage these problems, or use Java 8's computeIfAbsent. But the easiest way is to just use Table as above.
If you make your own class to use instead of Table, it would be something like:
public class DoubleMap<R, C, V> {
private final Map<R, Map<C, V>> backingMap;
public DoubleMap() {
this.backingMap = new HashMap<>();
}
public V get(R row, C column) {
Map<C, V> innerMap = backingMap.get(row);
if(map == null) return null;
else return innerMap.get(column);
}
public void put(R row, C column, V value) {
Map<C, V> innerMap = backingMap.get(row);
if(innerMap == null) {
innerMap = new HashMap<C, V>();
backingMap.put(row, innerMap);
}
innerMap.put(column, value);
}
}
You would use this class by doing:
DoubleMap<String, String, Object> map = new DoubleMap();
Note that this answer has a lot less features than the Guava version.
Getting a Value from a Map
If I understand your question, then with an index a and b that might look like (guarding against null with a ternary or Conditional Operator ? :),
Object obj = (map.get("a") == null) ? null : map.get("a").get("b");
Using a Generic Type
And you might be more specific, like
Map<String, Map<String, Something>> map = new HashMap<>();
Something s = (map.get("a") == null) ? null : map.get("a").get("b");
Adding values to the Map
Assuming you want to add your Something value to the map that could be done with something like,
Map<String, Map<String, Something>> map = new HashMap<>();
if (map.get("a") == null) {
map.put("a", new HashMap<>());
}
map.get("a").put("b", value);
If you don't need regular access to the entire "row", but just quick access to each cell you can use the built-in Map.Entry as your key:
Map<Map.Entry<String, String>, Object> table = new Map<>();
table.put(new Map.SimpleEntry("index1", "index2"), "Hello world");
Alternatively, if you're willing to go with something third-party, several someones have already implemented tuples for Java.
If you are in a situation where you cannot pull in a third-party library easily, but you don't like the semantics of Map.Entry (which is written in terms of keys and values) you can write your own Pair class to have the same effect.
As my understanding, you can do like:
Map<String, Map<String, Object> map= new HashMap();
Map<String, Object> subMap = map.get("index1");
if(subMap != null) {
Object obj = subMap.get("index2");
}
The best solution probably depends on how this map is intended to be used:
Is it used in a limited scope, or is it part of a public API?
Are the "indices" always of type String, or do they have to be generic?
Will it always be two indices, or may you need more indices later?
...
A pragmatic solution focussed on the question as you described it would be to introduce a StringPair class that can be used for indexing. This saves you from the hassle of doing 2D-lookups of inner maps (and possible cleanups when the inner maps become empty!), does not require any third-party libraries, and is readable and efficient.
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
public class StringPairMapTest
{
public static void main(String[] args)
{
Map<StringPair, Object> map = new LinkedHashMap<StringPair, Object>();
map.put(StringPair.of("A","B"), 12);
map.put(StringPair.of("C","D"), 34);
System.out.println(map.get(StringPair.of("A","B")));
System.out.println(map.get(StringPair.of("C","D")));
System.out.println(map.get(StringPair.of("X","Y")));
}
}
class StringPair
{
private final String s0;
private final String s1;
static StringPair of(String s0, String s1)
{
return new StringPair(s0, s1);
}
private StringPair(String s0, String s1)
{
this.s0 = s0;
this.s1 = s1;
}
#Override
public String toString()
{
return "("+s0+","+s1+")";
}
#Override
public int hashCode()
{
return Objects.hash(s0, s1);
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StringPair other = (StringPair) obj;
return Objects.equals(s0, other.s0) && Objects.equals(s1, other.s1);
}
}
Generalizations to a Pair<T> or Tuple<S,T> would be possible, of course, but this did not seem to be what you have been looking for...

Sorting of Map based on keys

This is not basically how to sort the HashMap based on keys. For that I could directly use TreeMap without a wink :)
What I have at the moment is
Map<String, Object> favoritesMap = new HashMap<String, Object>();
and its contents can be
["Wednesdays" : "abcd"]
["Mondays" : "1234"]
["Not Categorized" : "pqrs"]
["Tuesdays" : "5678"]
I want to sort the HashMap based on keys and additional to this I need "Not Categorized" to be the last one to retrieve.
So expected while iterating over keySet is
["Mondays", "Tuesdays", "Wednesdays", "Not Categorized"] i.e. sorted on keys and "Not Categorized" is the last one
Thought of going for HashMap while creating and at the end add ["Not Categorized" : "pqrs"] but HashMap does not guarantee the order :)
Any other pointers for the solution?
Are you specifically excluding TreeMap for some external reason? If not you could obviously use TreeMap with a specially made Comparator.
Have you considered any of the other SortedMaps?
If TreeMap is definitely out I would extend HashMap and make it look like there is always one more entry but that is certainly not a trivial piece of work. You should have a very good reason not to use a SortedMap before going down this road.
Added
Here is an example of how you can make a particular entry always sort to the end using a TreeMap:
// This key should always appear at the end of the list.
public static final String AtEnd = "Always at the end";
// A sample map.
SortedMap<String, String> myMap =
new TreeMap<>(
new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.equals(AtEnd) ? 1 : o2.equals(AtEnd) ? -1 : o1.compareTo(o2);
}
});
private void test() {
myMap.put("Monday", "abc");
myMap.put("Tuesday", "def");
myMap.put("Wednesday", "ghi");
myMap.put(AtEnd, "XYZ");
System.out.println("myMap: "+myMap);
// {Monday=abc, Tuesday=def, Wednesday=ghi, Always at the end=XYZ}
}
I wonder if you are looking for some variant of that?
You can achieve this by using LinkedHashMap as it guarantees to return results in the order of insertion.
Also check the following post to understand difference between map types.
Difference between HashMap, LinkedHashMap and TreeMap
Or just a create a custom class which holds a different key than the value. Sort according to the key of that class. For your case make the key same value as the day, and for "Not Categorized" case ensure that its key starts later than any of the other keys, for example make it "Z_Not Categorized".
public ComplexKey
{
String key;
String value;
}
ComplexKey monday = new ComplexKey("monday", "monday");
ComplexKey notCategorized = new ComplexKey("Z_Not Categorized", "Not Categorized");
Then you can write a custom comparator which sort the values according to the key of complexKey class.
In your case I would use a TreeMap:
Map<DayOfWeek, Object> favoritesMap = new TreeMap<>();
where DayOfWeek is a class you declare like:
class DayOfWeek implements Comparable<DayOfWeek> {
as it's not convenient to sort days of wooks as strings.
In fact, the keys are always sorted. If you output the map a couple of times, you will find that the result remains the same.
First I'll gossip again on hashing:
The reason is hashing. Each object has hashCode() method. The hash space is like a large array which contains all the possible hash values as indices. When a new element is inserted into a HashSet or a new pair is put into a HashMap, it is placed in the hash space according to its hash code. If two elements have the same hash code, they will be compared with equals() method, if unequal, then the new element will be placed next to it.
Then if you know what happens there, you can implement some code like below:
import java.util.*;
class MyString {
private String str;
public MyString (String str) {
this.str = str;
}
public String toString () {
return str;
}
public boolean equals (Object obj) {
if (obj.getClass().equals(MyString.class)) {
return obj.toString().equals(str);
}
return false;
}
public int hashCode () {
if (str.equalsIgnoreCase("Not Categorized")) {
return Integer.MAX_VALUE;
} else if (str.hashCode() == Integer.MAX_VALUE) {
return 0;
}
return str.hashCode();
}
}
public class Test {
public static void main (String args[]) {
Map<MyString, String> m = new HashMap<MyString, String>();
m.put(new MyString("a"), "a");
m.put(new MyString("c"), "c");
m.put(new MyString("Not Categorized"), "NC");
m.put(new MyString("b"), "b");
Set<MyString> keys = m.keySet();
for (MyString k : keys) {
System.out.println(m.get(k));
}
}
}
The result is "Not Categorized" always comes at last. The reason is simple: it's hash value is always the maximum of integer.
The reason I create a String wrapper class is String class is final, it can't be extended. So in this way, you would have your class structure a little change, but not much.
It is possible to use TreeMap, though it would be less efficient:
public static void main (String args[]) {
Map<String, String> m = new TreeMap<String, String>(new Comparator<String>() {
public int compare (String s1, String s2) {
if (s1.equals(s2)) {
return 0;
}
if (s1.equalsIgnoreCase("Not Categorized")) {
return 1;
}
if (s2.equalsIgnoreCase("Not Categorized")) {
return -1;
}
if (s1.hashCode() > s2.hashCode()) {
return 1;
} else if (s1.hashCode() < s2.hashCode()) {
return -1
} else {
return 0;
}
}
public boolean equals (Object obj) {
return false;
}
});
m.put("a", "a");
m.put("c", "c");
m.put("Not Categorized", "NC");
m.put("b", "b");
Set<String> keys = m.keySet();
for (String k : keys) {
System.out.println(m.get(k));
}
}
The result is the same. It will sort all the elements, but it won't change the hashing order of other strings, it only ensures "Not Categorized" always comes to be the largest one.

Initializing Hashtables in Java?

In C# you can initialize Hashtables (and many other types of objects) using code like this -
Hashtable table = new Hashtable {{1, 1}, {2, 2}};
Is there anything like this in Java or do you have to just declare the Hashtable first and then manually put items in it one by one?
This is answered elsewhere but you can use an anonymous subclass:
new HashMap<Integer, Integer>() {{ put(1, 1); put(2, 2); }};
Lot's of boiler plate, but still a one-liner :). This will, unfortunately, also complain about the missing serialVersionUID constant which you can either add or ignore the warning on.
This is called an instance initializer block, more information here.
In Google Guava, if you want an immutable map you can use:
Map<K,V> m = ImmutableMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
up to 5 Key/Value pairs.
Beyond that, you can use their ImmutableMap.Builder class:
ImmutableMap<String, Integer> WORD_TO_INT = ImmutableMap.builder()
.put("one", 1)
.put("two", 2)
.put("three", 3)
.build();
Still not nearly as nice as in C#, but the fluent API is a bit helpful.
If you need to initialize a HashMap (HashTable is obsolete) you can use an static initialization block.
Example:
private static Map<String, String> map;
static {
map = new HashMap<String, String>();
map.put("name1", "value1");
map.put("name2", "value2");
....
}
Hope this helped, have Fun!
Another answer (besides the obvious "no -- no native language way to do this"):
Create a Tuple class with a static factory method with a fancy-pants "_" name for brevity:
import java.util.Map;
import java.util.HashMap;
class Tuple<T1,T2> {
private T1 t1;
private T2 t2;
public Tuple(T1 t1, T2 t2) {
this.t1 = t1; this.t2 = t2;
}
public T1 getT1() {return t1;}
public T2 getT2() {return t2;}
static public <X,Y> Tuple<X,Y> _(X t1, Y t2) { return new Tuple<X,Y>(t1,t2); }
static public <X,Y> Map<X,Y> mapFor(Tuple<X,Y>... tuples) {
Map<X,Y> map = new HashMap<X,Y>();
for( Tuple<X,Y> tuple: tuples ) {
map.put(tuple.getT1(), tuple.getT2());
}
return map;
}
public static void main(String[] args) {
Map<String,Integer> map = Tuple.mapFor( _("A", 1), _("B", 2), _("C",3));
}
}
If you want to allow variations on what kind of backing map is produced, you can just pass that in instead:
static public <X,Y> Map<X,Y> mapFor(Map<X,Y> map, Tuple<X,Y>... tuples) {
for( Tuple<X,Y> tuple: tuples ) {
map.put(tuple.getT1(), tuple.getT2());
}
return map;
}
Try this:
Hashtable<Integer, String> ht = new Hashtable<Integer, String>(){
{
put(1,"One");
put(2,"Two");
put(3,"Three");
}
};
There's a thing called double brace initialization, which isn't as nice...
Hashtable table = new Hashtable() {
{
table.put(1, 1);
table.put(2, 2);
}
};
You could even specify using anonymous array notation, and then iterate over it yourself, like this:
Hashtable table = new Hashtable() {
{
for (int[] entry : new int[][] { { 1, 1 }, { 2, 2 } }) {
table.put(entry[0], entry[1]);
}
}
};
Perhaps make a utility function if you're really missing python :)
One of C#'s features which I quite like is its ability to initialize inline like that. Unfortunately, Java doesn't have this feature.
The Java Hashtable does not have any constructors which allow for this either. See a list of its constructors in the Java API documentation:
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Hashtable.html
A safe approach is to use ImmutableMap.of from Guava and optionally wrapped with newHashSet if mutability is needed:
Maps.newHashMap(ImmutableMap.of(k1, v1, ...));
Here's a self-written map builder for inline use, e.g. newMap(1, "one", 2, "two", 3, "three").
public static <K, V> Map<K, V> newMap(final K key, final V value, final Object... elements) {
Preconditions.checkNotNull(key);
Preconditions.checkArgument(elements.length % 2 == 0, "Array length can't be " + elements.length);
final HashMap<Object, Object> map = Maps.newHashMap();
map.put(key, value);
for (int i = 0; i < elements.length; i += 2) {
map.put(elements[i], elements[i + 1]);
}
return (Map<K, V>) map;
}
i suggest something like this:
String[] names={"albert","john","michel"};
int[] id={1234,2345,3456};
Hashtable<int,String> persons = new Hashtable<int,String>();
for(int i=0;i<3;i++)
{
persons.put(id[i],names[i]);
}
This is not possible in Java. We all suffer from that.

Increment an Integer within a HashMap

Do I have to return the object and then put a new one in ? Or can I just directly increment ?
Integer temp = myMap.get(key);
temp++;
myMap.put(key, temp);
there is no way to just do this (this doesn't work) :
myMap.get(key)++;
This is the shortest code that does this job.
myMap.put(key, myMap.get(key) + 1)
I think it is not too long.
In Java 8 there are new methods on Map which you can use with lambdas to solve this. First alternative, compute:
a.compute(key, (k, v) -> v+1);
Note that this only works if the hash is initialized for all possible keys.
If this is not guaranteed you can either change the above code to:
a.compute(key, (k, v) -> v == null ? 1 : v + 1);
Or use the merge method (which I would prefer):
a.merge(key, 1, (a, b) -> a + b);
Maybe there are more lambda based methods I am not aware of.
You can use a mutable integer such as AtomicInteger.
Map<Key, AtomicInteger> myMap = new HashMap<Key, AtomicInteger>();
myMap.get(key).incrementAndGet();
Or you can use Trove4j which supports primitives in collections.
TObjectIntHashMap<Key> myMap;
myMap.increment(key);
Do I have to return the object and then put a new one in ?
As long as you use the Integer wrapper class yes, because it's immutable. You could use a mutable wrapper class instead, even one that has an increment() method. However, you then lose the ability to use autoboxing and autounboxing on the values.
You can't directly increment it, because it is immutable. You have to increment it and put the new object back.
Auto boxing is also interfering here. In fact what's happening is something similar to:
Integer i1 = getFromMap();
i1 = Integer.valueOf(++ i1.intValue());
So here your reference points to a new object. You have to put that object back in the map, under the same key.
As Integer are immutable, yes, you have to do it that way.
If you really want to increment it directly, you'll have to write your own mutable class.
If you have to do this more than twice you'd prefer to create a tiny class like:
public class MappedCounter {
private Map<String, Integer> map = new HashMap<String, Integer>();
public void addInt(String k, int v) {
if (!map.containsKey(k)) map.put(k, v);
else map.put(k, map.get(k) + v);
}
public int getInt(String k) {
return map.containsKey(k) ? map.get(k) : 0;
}
public Set<String> getKeys() {
return map.keySet();
}
}
Here are solutions using a Map (Java 8+), and a primitive Map and Bag using Eclipse Collections (EC).
JDK Map
Map<String, Integer> map = new HashMap<>();
map.merge("item", 1, Integer::sum);
Integer count = map.getOrDefault("item", 0);
EC Primitive Map
MutableObjectIntMap<String> map = ObjectIntMaps.mutable.empty();
map.addToValue("item", 1);
int count = map.getIfAbsent("item", 0);
EC Bag
MutableBag<String> bag = Bags.mutable.empty();
bag.add("item");
int count = bag.occurrencesOf("item");
The benefit of the primitive Map or Bag (which wraps a primitive Map) is that there is no boxing of the count values, and adding is explicit in both method names (addToValue / add). A Bag is a better data structure IMO if you want to simply count things.
Note: I am a committer for Eclipse Collections.
First of all: be aware of unboxing: the temp is from type Integer. But the operation ++ is implemented for int. So temp is unboxed to type int. This means if temp is null you run in a NullPointerException.
And you have to do it like you discripted in your first code block.
I use the below code and it works but at the beginning you need to define a BiFunction describing that the operation is incrementing by 1.
public static Map<String, Integer> strInt = new HashMap<String, Integer>();
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> bi = (x,y) -> {
if(x == null)
return y;
return x+y;
};
strInt.put("abc", 0);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abcd", 1, bi);
System.out.println(strInt.get("abc"));
System.out.println(strInt.get("abcd"));
}
output is
3
1
Just for completeness in Java 8 there is a longAdder which brings some benefits in comparison to AtomicInteger (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html)
final Map<WhatEver, LongAdder> result = new HashMap<>();
result.get(WhatEver).increment();
This should work
// If the key you want to add does not exist then add it as a new key
// And make the value 1
if (map.get(key) == null) {
map.put(key, 1);
} else {
// If the key does exist then replace the key's value with it's
// Original value plus one
map.put(key, map.get(key) + 1);
}
Found this to be the best way, avoiding NPE.
Map<Integer, Integer> map = new HashMap<>();
map.put(5, map.getOrDefault(5, 0) + 1);
System.out.println(map.get(5));
Output:
1

Accessing the last entry in a Map

How to move a particular HashMap entry to 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?
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
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.
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.
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
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.
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)) );
}
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);
}

Categories