Data structure that has key-value mapping as well as ordering - java

I need a data structure that provides key-value mappings, like a Map, but that also allows me to fetch the key based on an (int) index (e.g. myKey = myDS.get(index)), without having to iterate over the data structure to get the key at the desired index.
I thought of using LinkedHashMap, but I don't see a way to get the key at a given index. Am I missing something in LinkedHashMap? Or is there another data structure I can use?
EDIT:
This is not a duplicate. The correct answer to the other question is to use some sort of SortedMap; however, that is not the correct answer to this question, since I'd like to be able to retrieve an Entry from the data structure via an Integer index, which is not supported in any Java library.

LinkedHashMap provides a hash table/doubly linked list implementation of the Map interface. Since it extends HashMap, it's still backed by an array, but there is also a doubly-linked list of Entry objects to ensure that the iteration order is predictable.
So, basically what it means is that when you iterate through the map like so:
for (Map.Entry<keyType,valueType>> entry : linkedHashMap.entrySet())
{
System.out.println("Key: " + entry.getKey().toString() +
" Value: " + entry.getValue.toString());
}
it will print in the order that you added the keys, as opposed to a non-linked Map, which will not print in insertion order. You cannot access the elements of the array like you want to, because the array that backs the hash is not in order. Only the doubly linked list is ordered.
Solution:
What you are looking for is a LinkedMap from Apache Commons.

AFAIK, there is no single data structure that will do this. There is certainly not one in the standard Java collection suite.
Also LinkedHashMap is not the solution because you cannot efficiently index a LinkedHashMap.
If you want to do index-based lookup as well as keep-based lookup, solution needs to be a combination of two data structures.
A Map<Key, Value> and an ArrayList<Value> is the simpler approach, but it has a couple of problems:
- Insertion and deletion of values from the ArrayList is expensive, unless you are inserting / deleting at the tail end of the list.
- Insertion and deletion makes the list positions unstable,.
If you want stable indexes and scalable insertion and deletion, then you need a Map<Key, Value> and a Map<Integer, Value> ... and a way to manage (i.e. recycle) the index values.
The Apache Commons LinkedMap class is a possible solution, except that it suffers from the problem that index values are not stable in the face of insertions and deletions.

How about using:
Map<String, String> map = new LinkedHashMap<String, String>();
List<Entry<String, String>> mapAsList = new ArrayList<Map.Entry<String,String>>(map.entrySet());
mapAsList.get(index);

I do not believe there is a collection for this; collections are either based on the idea that you want to know exactly where an element is (lists) or that you want quick access based on some key or criteria (maps); doing both would be very resource-intensive to maintain.
Of course, you can make something like this, as rocketboy's answer suggests, but I'm guessing it's not really possible to make efficient.

There is no direct DS in the standard Java Collections API to provide a indexed map. However, the following should let you achieve the result:
// An ordered map
Map<K, V> map = new LinkedHashMap<K, V>();
// To create indexed list, copy the references into an ArrayList (backed by an array)
List<Entry<K, V>> indexedList = new ArrayList<Map.Entry<K, V>>(map.entrySet());
// Get the i'th term
<Map.Entry<K,V>> entry = indexedList.get(index);
K key = entry.getKey();
V value = entry.getValue();
You might still want to retain the concerns of data persistence in the map separate from the retrieval.
Update:
Or use LinkedMap from Apache Commons as suggested by Steve.

Related

Why can't we just use its iterator or a for-each loop to visit the values in a java Map(HashMap or TreeMap) in order?

I am a beginner in Java. Please explain it as plain as possible.
I am putting a dummy code, because this site didn't let me post the question without this:
public void printSorted(PrintStream out) {
TreeMap<Integer,String> map2 = new TreeMap<Integer,String>();
for(Map.Entry<String,Integer> entry : concord.entrySet()){
map2.put(entry.getValue(), entry.getKey());
}//NavigableMap nmap=treemap.descendingMap();
for(Map.Entry<Integer,String> entry2 : map2.descendingMap().entrySet()){
System.out.println(entry2.getValue()+ " " + entry2.getKey());
}
}
Why can't we just use its iterator or a for-each loop to visit the values in a java Map(HashMap or TreeMap) in order?
Because those two classes do not maintain the values in any order. (Apart from the implied order of the keys ... in the TreeMap case.)
Indeed, there is not even a requirement on the value type of a Map that it be orderable.
If you want / need the values of a map in order:
If the ordering you want is "entry insertion order" or "entry least recently used", then use a LinkedHashMap.
For other orderings, you can copy the list of values (or the set of entries) into a separate set and then sort it. (Or you can do the equivalent with streams without an explicit copy.) But the point is that the Map itself won't / can't maintain the ordering you have just created by sorting.
If you want to avoid the cost of repeated sorting, use a separate (incrementally updatable) data structure to keep a sorted list or set of the values.
P.S. If you actually are asking about iterating the keys or entries in order, then TreeMap already does that. And ...
why do we use Map iterator and not use for/each loop?
We can do either. Both work. And indeed, a for each loop uses the Iterator under the covers.
You only need to use an iterator explicitly if you want to explicitly call methods on the iterator; e.g. Iterator::remove()

A Collection that associates keys to values, and where both a key and a value can be obtained by index

By indexed I mean keys and values can be accessed via an index representing the order in which they were inserted into the collection.
I need a collection that behaves like a Map<K, V>, but also a List<K>(Read-Only) and a List<V>(also Read-Only). My naive implementation is to wrap a HashMap<K, V> and 2 ArrayList, but that leads to massive data redundancy and poor perfomance. Then I thought about LinkedHashMap<K, V>, which would work a lot better in this case, but the getByIndex operations would not perform well, because that would require navigating the internal Linked Nodes, which for small quantities of data is perfectly acceptable, but I'm not exactly sure how will the list be used by client code.
In short, is there something that suits my requirements better than the alternative?
EDIT: If I had something like pointer arithmetics and low level functions like memcpy and a runtime sizeof operator resolving the sizes of K and V, then maybe I could come up with a very efficient implementation. Are there any equivalents to any of that in Java?
I can suggest you few indirect ways.
You can create HashMap < Integer,HashMap< K,V > >. You can insert in this map keeping order as key and then can put the Key-value pair HashMap as value.
You can simply have a single ArrayList<K> and a HashMap<K,V>. For each entry to the map you can insert the key in the array list.
You can use (as you have said in the question itself) LinkedHashMap and can get the iterator or can use for each enhanced for loop for iteration. This way of iterating is efficient and for each step of iteration entire list is not iterated. But you can only iterate and not get the random indexed entry.
If third-party libraries are fair game, Guava's ImmutableMap does this nicely if you don't need mutation. Once it's created, you can use map.entrySet().asList(), map.keySet().asList(), and map.values().asList() to get, in O(1), random-access lists of the entries, keys, and values that support get(index) in O(1).

Sorting collection in java

I have a HashMap which has keys as Date in Strings and value as an ArrayList of custom objects. I want to sort this hashmap on the basis of key. How can I do that?
HashMap<String,List<ClassName>> hashmap = new HashMap<String,List<ClassName>>();
When Hashmap is like this:
{"2015/07/15 : List("Object1","object2","object3")
"2015/07/14 :List("Object4" , "Object5")}
Please suggest.
You can use TreeMap instead of a HashMap . The TreeMap implements the Sorted Map interface.
As well as using a sorted map (as others have suggested) you can easily sort the keys when you use them rather than when you insert them.
For example, in Java 8:
treeMap.keySet().stream().sorted().forEach(System.out:println);
A nice thing about this is that it's easy to sort using different comparators without changing the collection.
For example, if you wanted to to sort by the number of items in the list value:
treeMap.keySet().stream().sorted(Comparator.comparingInt(k -> treeMap.get().size()))
This method is good for situations in which you insert and change values in the map often and then occasionally need the keys sorted for a particular operation. You get the performance of a HashMap but the flexibility to sort however you want on use.
You can use TreeMap, if you need the sorted map.
If you don't want to use TreeMap, then get the key and sort it as below.
HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
map.put("2015/07/15", list);
map.put("2015/07/17", list1);
map.put("2015/07/16", list1);
ArrayList<String> keyset = new ArrayList<String>(map.keySet());
Collections.sort(keyset);
First thing is you can use TreeMap when you need a sorted map. However you are storing date value as Strings. Then it become harder to compare each. So i recommend to use java.util.Date instead of String. You can use a date formatter when you adding to the map. Use following code.
TreeMap<Date, List> treeMap = new TreeMap<>();
You can specify the Comparator in the constructor of the TreeMap. So it's easy to sort up things according to your custom order.

Creating Dictionary in java?

Everywhere on net, here is the way
Map<String, String> map = new HashMap<String, String>();
map.put("dog", "type of animal");
System.out.println(map.get("dog"));
My point is should it not be Treemap considering dictionary has to be sorted? Agreed lookup wont be optimized in case of Treemap but considering sorting its best data structure
UPDATE :- one more requirement is return the lexicographically nearest word if the word searched is not present . I am not sure how to achieve it?
If you need the map sorted by its keys, then use TreeMap, which "...provides guaranteed log(n) time cost for the containsKey, get, put and remove operations." If not, use the more general HashMap (which "...provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets..."), or one of the other Map implementations, depending on your need.
If you want to get value for given key and if the probability of having the exact match of key in hashmap is less then using hashmap wont give you benefit of direct lookup.
If using TreeMap you can get list of keys which is already ordered and can perform a binary search on the list. While searching compare key lexicographically. Continue binary search till the lexicographic distance between two keys is minimum or 0.
Dictionary is no longer a term used in the language. You'll get multiple answers.
I know that Objective-C uses a class called Dictionary that is as a Key / Value data structure. The fact that it's named Dictionary leads me to believe that is the ordering of the objects, I imagine the Key has to be a string or char
So, it depends on the entire question.
When someone says they want to create a Key/Value data structure that is ordered alphabetically, or a "Dictionary", the answer is:
TreeMap<String, Object> map = new TreeMap<>()
If someone is asking how to create a Key/Value object similar to a Dictionary in whatever language, they will likely get any of the java.util classes that implement the Map<K, V> interface, for example HashMap, TreeMap. A good answer would be a TreeMap.
In this case telling someone to use a HashMap is not debatable, because the answer is as vague as the question.

How to avoid items being re-ordered when put into java HashMap

I'm creating a new Map and pushing strings into it (no big deal) -but I've noticed that the strings are being re-ordered as the map grows. Is it possible to stop this re-ordering that occurs so the items in the map retain the order the were put in with?
Map<String,String> x = new HashMap<String, String>();
x.put("a","b");
x.put("a","c");
x.put("a","d");
x.put("1","2");
x.put("1","3");
x.put("1","4");
//this shows them out of order sadly...
for (Map.Entry<String, String> entry : x.entrySet()) {
System.out.println("IN THIS ORDER ... " + entry.getValue());
}
If you care about order, you can use a SortedMap. The actual class which implements the interface (at least for most scenarios) is a TreeMap. Alternatively, LinkedHashMap also maintains its order, while still utilizing a hashtable-based container.
You can keep it with LinkedHashMap.
A HashMap in java is not sorted http://download.oracle.com/javase/1,5.0/docs/api/java/util/HashMap.html. If you want predictable iteration order use a LinkedHashMap instead: http://download.oracle.com/javase/1.4.2/docs/api/java/util/LinkedHashMap.html
Heres a good discussion on the difference: How is the implementation of LinkedHashMap different from HashMap?
The previous answers are correct in that you should use an implementation of Map that maintains ordering. LinkedHashMap and SortedMap each do these things.
However, the takeaway point is that not all collections maintain order and if order is important to you, you should choose the appropriate implementation. Generic HashMaps do not maintain order, do not claim to do so and cannot be set to do so.

Categories

Resources