ArrayMap put method pushes elements in strange order - java

I am using ArrayMap for first time in my project and I thought it works just like an array. I expected when I use .put method it inserts it at next index.
But in my case this is not true - after I added all elements one by one the first element I added ended up at index 4 which is kind of strange.
Here are the first three steps which I add elements:
1 - Salads:
2 - Soups:
3 - Appetizers:
So somehow on second step "Soup" element was inserted in index 0 instead of 1 as I was expecting, but strangely on third step "Appetizers" was inserted as expected after "Soup".
This is the code I am using to push key and value pair:
function ArrayMap<String, DMType> addElement(String typeKey, DMType type) {
ArrayMap<String, DMType> types = new ArrayMap<>();
types.put(typeKey, type);
return types;
}
Am I missing something about the behavior of ArrayMap?

Yeah it is misleading because of the name but ArrayMap does no gurantee order unlike arrays.
ArrayMap is a generic key->value mapping data structure that is
designed to be more memory efficient than a traditional HashMap.
ArrayMap is actually a Map:
public class ArrayMap extends SimpleArrayMap implements Map
If you want the Map functionality with order guranteed use LinkedHashMap instead.
LinkedHashMap defines the iteration ordering, which is normally the
order in which keys were inserted into the map (insertion-order).
documentation

I thought it works just like an array
No, it works like a map, because it is a map. It is similar to a HashMap, but more memory efficient for smaller data sets.
It's order shouldn't and doesn't matter. Under the hood, it is implemented using
an array which has an order since arrays do. This inherently gives the ArrayMap an order, but that is not part of it's API anyway. Just like which memory slot your Java objects are in, you shouldn't care about the order here either.

It doesn't work as an array, I don't see Array in the name but Map and the documentation clearly states that behaves as a generic key->value mapping, more efficient (memory wise) than traditional HashMap implementation.
Actually I don't see why you care about the order compared to the insertion one. Data is private inside the class and you have no way to obtain the element by the index, so you are basically wondering about a private implementation which is irrelevant for its usage.
If you really want to understand how it stores its data you should take a look at the source code.

ArrayMap does NOT work like an Array, instead, it works like a HashMap with performance optimizations.
The internal sequence of the key-value pair is not guaranteed as it is NOT part of the contract.
In your case, what you really want to use is probably an ArrayList<Element>, where the Element class is defined like this:
public class Element{
private final String typeKey;
private final DMType type;
public Element(String typeKey, DMType type){
this.typeKey = typeKey;
this.type = type;
}
}
If you don't want a new Class just to store the result, and you want to keep the sequence, you can use a LinkedHashMap<String, DMType>. As the document specifies:
Class LinkedHashMap
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)

Related

Navigable Map - Sorted Map yet un-ordered

Hi everyone please help, I discovered the NavigableMap today and initially it worked for me until I did the following.
When I populate the NavigableMap, notice from the end of the string value, I have added the values 1-4 and then 5.
The string layout is for example "23816012269<1>", the last value <#> makes this unique because I am using it later in the code.
All values up until 4 are duplicates (23816012269) , then 23806012269<5> is a new value prefixed with a different string/number and I will continue with new duplicates (suffixed with the next sequence <5> <#> number etc).
The problem is, when I populate the NavigableMap in the order that you see below, it puts ("238060122695",...) at the first entry of the NavigableMap, even though this was added to the NavigableMap very last.
Now when I use the NavigableMap.firstEntry().getKey() it reads "238060122695" as the first entry.
I need to read 238160122691 as the first entry because this was added to the map as the first entry.
Delving into the string value I know that 2380 comes before 2381, regardless, why does it treat it as the first entry when I added it last, is it because of the SortedMap interface that does this.? How do I avoid
This is code in the order being executed:
NavigableMap.put("238160122691", arrayListValue);
NavigableMap.put("238160122692", arrayListValue);
NavigableMap.put("238160122693", arrayListValue);
NavigableMap.put("238160122694", arrayListValue);
NavigableMap.put("238060122695", arrayListValue);
A NavigableMap is sorted:
A SortedMap extended with navigation methods returning the closest matches for given search targets. [...]
A NavigableMap may be accessed and traversed in either ascending or descending key order.
If you want entries to be in insertion order, use a LinkedHashMap:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
Delving into the string value I know that 2380 comes before 2381, regardless, why does it treat it as the first entry when I added it last, is it because of the SortedMap interface that does this?
There seems to be a misunderstanding of what NavigableMap and its implementing classing like TreeMap are doing. They do not sort the elements in order of insertion, but by their natural order (i.e. how they compareTo each other), or given another Comparator.
How do I avoid?
Well, you could wrap your string keys into a data structure that also has a number showing when it was inserted into the map, and provide a Comparator or compareTo method that sorts by that number. But more likely, NavigableMap is just not the right data structure for you. Have you considered just using a List or Queue, or maybe a LinkedHashMap as suggested in comments.
Thank you All, I have moved back to the LinkedList as suggested by everyone.
I then found the use of the ListIterator to find the the next entry and stored that in a variable to compare to the next Iteration.
ListIterator<Map.Entry<String, ArrayList<DataModel>>> listIterator = new LinkedList(myHashMap.entrySet()).listIterator();
String previousk, nextk = null;
Boolean entered = false;
while (listIterator.hasNext())
{
if(entered){
previousk = nextk.substring(0,12);
if(previousk != nextk.substring(0,12)){
*Set the flag here to be used*
}
}
nextk = listIterator.next().getKey();
entered = true;
System.out.println(nextk);
}

Are there any map implementations in Java that will give me a map where the entries are ordered in the same way I put them in?

What I would like to do is this:
for (Entry<Foo, Foo> myEntry : myMap.entryList())
{
if (someCondition(myEntry.getKey()))
{
doSomething(myEntry.getValue())
}
}
The order is important because myEntry.getValue() will point to a key that will be reached later on. Is there a Map implementation that guarantees the order of the entries and allows me to iterate over the entries in that order? I found SortedMap, but it looks like SortedMaps sort the keys. This isn't quite what I need. I simply want to get my entries out of my map in the same order that I put them in.
For those wondering what I'm trying to do, myMap represents a hierarchy of Foo objects. When the condition is met in 'someCondition', I mark the parent of the Foo (which happens to be the value of the entry). Later on when the parent becomes a key, I'll know that I've marked it before and will take action accordingly. Right now I've created a recursive 'markParent' call, but if I could iterate over the map in a known order, I wouldn't need to do that.
You mean like LinkedHashMap? ;-)
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
(My emphasis)
FWIW, how I got there, to help if you need to find similar information in the future: I had a vague recollection one of the JDK classes did that, so went to Map and read through the list of "Known Implementing Classes." In this case, I knew the name when I saw it, but I figure even if I hadn't known it, LinkedHashMap suggests order, so... :-)
LinkedHashMap will maintain the order of insertion which using LinkedList internally. And if you want to define any custom order, you can use SortedMap like TreeMap instead.

What is LinkedHashMap<k, v>?

Ok so i am new to these HashMaps but have some idea about LinkedLists and HashMaps.
It would be great if you could give me some simple explanation regarding LinkedHashMap and as in the titile does this mean we are explicitly defining it to be of some type?
A LinkedHashMap is a combination of
hash table and linked list. It has a
predictable iteration order (a la
linked list), yet the retrieval speed
is that of a HashMap. The order of the
iteration is determined by the
insertion order, so you will get the
key/values back in the order that they
were added to this Map. You have to be
a bit careful here, since re-inserting
a key does not change the original
order.
k stand for Key and v for Value.
/*
Simple Java LinkedHashMap example
This simple Java Example shows how to use Java LinkedHashMap.
It also describes how to add something to LinkedHashMap and how to
retrieve the value added from LinkedHashMap.
*/
import java.util.LinkedHashMap;
public class JavaLinkedHashMapExample {
public static void main(String[] args) {
//create object of LinkedHashMap
LinkedHashMap lHashMap = new LinkedHashMap();
/*
Add key value pair to LinkedHashMap using
Object put(Object key, Object value) method of Java LinkedHashMap class,
where key and value both are objects
put method returns Object which is either the value previously tied
to the key or null if no value mapped to the key.
*/
lHashMap.put("One", new Integer(1));
lHashMap.put("Two", new Integer(2));
/*
Please note that put method accepts Objects. Java Primitive values CAN NOT
be added directly to LinkedHashMap. It must be converted to corrosponding
wrapper class first.
*/
//retrieve value using Object get(Object key) method of Java LinkedHashMap class
Object obj = lHashMap.get("One");
System.out.println(obj);
/*
Please note that the return type of get method is an Object. The value must
be casted to the original class.
*/
}
}
/*
Output of the program would be
1
*/
It is a hybrid of two data structures, a LinkedList, where insertion order is preserved by adding elements to the end of a list of nodes which have access to their immediate neighbours, and a HashMap, or a Map that uses an array of bucket Lists, where a modulus division remainder of the key's hashcode() determines the starting bucket to query for the equals() method of the keys that lie in that bucket's list of contents.
The advantage is that you can walk the existing elements in a HashMap in order of insertion, due to the LinkedList nature, and you can quickly jump to the correct bucket in a key lookup (saving a lot of time for a large collection) if you have the key of the element.
This is called generics. k and v must be replaced with the actual type you want to store.
To create a HashMap that maps integers on strings you would write:
LinkedHashMap<Integer,String>
LinkedHashMap keys are similar to ArrayLists or arrays in the way they are stored in the order that they are inserted.
Normal HashMaps are sorted by their hash code.
k = key
v = value
They can be any type.
The biggest difference is LinkedHashMap is ordered. If you use an iterator the keys and values will be in the same order they were added to the map. HashMap has no guarantee of the order they are returned.
Read about Generics in Java on Wikipedia.

Java Map question

I have one Map that contains some names and numbers
Map<String,Integer> abc = new HashMap<String,Integer>();
It works fine. I can put some values in it but when I call it in different class it gives me wrong order. For example:
I putted
abc.put("a",1);
abc.put("b",5);
abc.put("c",3);
Iterator<String> iter = abc.keySet().iterator();
while (iter.hasNext()) {
String name = iter.next();
System.out.println(name);
}
some time it returns the order (b,a,c) and some time (a,c,b).
What is wrong with it? Is there any step that I am missing when I call this map?
Edit:
I changed to HashMap and result is still same
The only thing that's wrong is your expectations. The Map interface makes no guarantees about iteration order, and the HashMap implementation is based on hash functions which means the iteration order is basically random, and will sometimes change completely when new elements are added.
If you want a specific iteration order, you have thee options:
The SortedMap interfaces with its TreeMap implementation - these guarantee an iteration order according to the natural ordering of the keys (or an ordering imposed by a Comparator instance)
The LinkedHashMap class iterates in the order the elements were added to the map.
Use a List instead of a Map - this has a well-defined iteration order that you can influence in detail.
I think you need LinkedHashMap.
A TreeMap will always have keys in their natural order (unless you provide a comparator) If you are seeing the order any differently it will be the way you are looking at the map and what you are doing with it. If in doubt, use a debugger and you will see the order is sorted.
If you wish to get map values in the same order you used to insert them use LinkedHashMap instead.

Finding the highest-n values in a Map

I have a large map of String->Integer and I want to find the highest 5 values in the map. My current approach involves translating the map into an array list of pair(key, value) object and then sorting using Collections.sort() before taking the first 5. It is possible for a key to have its value updated during the course of operation.
I think this approach is acceptable single threaded, but if I had multiple threads all triggering the transpose and sort frequently it doesn't seem very efficient. The alternative seems to be to maintain a separate list of the highest 5 entries and keep it updated when relevant operations on the map take place.
Could I have some suggestions/alternatives on optimizing this please? Am happy to consider different data structures if there is benefit.
Thanks!
Well, to find the highest 5 values in a Map, you can do that in O(n) time where any sort is slower than that.
The easiest way is to simply do a for loop through the entry set of the Map.
for (Entry<String, Integer> entry: map.entrySet()) {
if (entry.getValue() > smallestMaxSoFar)
updateListOfMaximums();
}
You could use two Maps:
// Map name to value
Map<String, Integer> byName
// Maps value to names
NavigableMap<Integer, Collection<String>> byValue
and make sure to always keep them in sync (possibly wrap both in another class which is responsible for put, get, etc). For the highest values use byValue.navigableKeySet().descendingIterator().
I think this approach is acceptable single threaded, but if I had multiple threads all triggering the transpose and sort frequently it doesn't seem very efficient. The alternative seems to be to maintain a separate list of the highest 5 entries and keep it updated when relevant operations on the map take place.
There is an approach in between that you can take as well. When a thread requests a "sorted view" of the map, create a copy of the map and then handle the sorting on that.
public List<Integer> getMaxFive() {
Map<String, Integer> copy = null;
synchronized(lockObject) {
copy = new HashMap<String, Integer>(originalMap);
}
//sort the copy as usual
return list;
}
Ideally if you have some state (such as this map) accessed by multiple threads, you are encapsulating the state behind some other class so that each thread is not updating the map directly.
I would create a method like:
private static int[] getMaxFromMap(Map<String, Integer> map, int qty) {
int[] max = new int[qty];
for (int a=0; a<qty; a++) {
max[a] = Collections.max(map.values());
map.values().removeAll(Collections.singleton(max[a]));
if (map.size() == 0)
break;
}
return max;
}
Taking advantage of Collections.max() and Collections.singleton()
There are two ways of doing that easily:
Put the map into a heap structure and retrive the n elements you want from it.
Iterate through the map and update a list of n highest values using each entry.
If you want to retrive an unknown or a large number of highest values the first method is the way to go. If you have a fixed small amount of values to retrieve, the second might be easier to understand for some programmers.
Personally, I prefer the first method.
Please try another data structure. Suppose there's a class named MyClass which its attributes are key (String) and value (int). MyClass, of course, needs to implement Comparable interface. Another approach is to create a class named MyClassComparator which extends Comparator.
The compareTo (no matter where it is) method should be defined like this:
compareTo(parameters){
return value2 - value1; // descending
}
The rest is easy. Using List and invoking Collections.sort(parameters) method will do the sorting part.
I don't know what sorting algorithm Collections.sort(parameters) uses. But if you feel that some data may come over time, you will need an insertion sort. Since it's good for a data that nearly sorted and it's online.
If modifications are rare, I'd implement some SortedByValHashMap<K,V> extends HashMap <K,V>, similar to LinkedHashMap) that keeps the entries ordered by value.

Categories

Resources