Is it possible to get list of values from HashMap as a reference
class MyCustomObject {
String name;
Integer id;
MyCustomObject(String name, Integer id){
this.name = name;
this.id = id;
}
}
HashMap<Integer, MyCustomObject> map = new LinkedHashMap<>();
map.put (1, new MyCustomObject("abc",1));
map.put (2, new MyCustomObject("xyz",2));
List<MyCustomObject> list = new ArrayList<>(map.values());
Log.i(TAG,"************ List from HashMap ************");
for (MyCustomObject s : list) {
Log.i(TAG,"name = "+s.name);
}
list.set(0,new MyCustomObject("temp",3));
Log.i(TAG,"************ List from HashMap after update ************");
for (MyCustomObject s : list) {
Log.i(TAG,"name = "+s.name);
}
Log.i(TAG,"************ List from HashMap ************");
List<MyCustomObject> list2 = new ArrayList<>(map.values());
for (MyCustomObject s : list2) {
Log.i(TAG,"name = "+s.name);
}
Output
**************** List from HashMap ***************
name = abc
name = xyz
**************** List from HashMap after update ***************
name = temp
name = xyz
**************** List from HashMap ***************
name = abc
name = xyz
Here if get list of values from HashMap it return deep-copy.
Update
My Requirement
I want list of values from HashMap because I want to access items using their position
I want to preserve order of values
If I modify anything in the extracted list then it should reflect in HashMap too
Please do tell, if any third party library provide such data structure, or what would be best approach to handle this situation
You are creating an new List based on the values of the Map :
List<MyCustomObject> list = new ArrayList<>(map.values());
That's what creates the copy of the values Collection, and changes in that List cannot be reflected in the original Map.
If you modify the Collection returned by map.values() directly (for example, map.values().remove(new MyCustomObject("abc",1))), it will be reflected in the contents of the original Map. You wouldn't be able to call set on the Collection, though, since Collection doesn't have that method.
Collection values()
Returns a Collection view of the values contained in this map. The
collection is backed by the map, so changes to the map are reflected
in the collection, and vice-versa.
So use a Collection and assign values() to it. Or the entrySet().
Try using the map entries which are backed by the map and which you get by calling entrySet(). A list of those almost works like you want it to do (although I'd still advocate you directly use map.put( key, updatedValue ).
Example:
Map<String, Integer> map = new HashMap<>();
map.put( "a", 1 );
map.put( "b", 2 );
//you create a list that's not backed by the map here but that isn't a problem
//since the list elements, i.e. the entries, are backed by the map
List<Entry<String, Integer>> entryList = new ArrayList<>(map.entrySet());
entryList.get(0).setValue( 5 );
System.out.println( map ); //prints: {a=5, b=2} (note that order is a coincidence here)
One final note though: as I already stated in my comment when dealing with a map order is not always deterministic (unless you know you're dealing with an ordered map like TreeMap) and thus using indices may introduces bugs or undesired behavior. That's why you'll want to at least check the key in most cases and thus you either need to use Map.Entry (which btw can't have its key altered, for good reasons) or use the key directly in which case you don't need a list/collection of values or entries anyways.
Related
I am having an arraylist which contains a list of numbers. I want to get all the values from the HashMap which has the keys which are in the array list.
For example say the array list contains 1,2,3,4,5,6,7,8,9 list
I want to get all the values for the keys 1,2,3,4,5,6,7,8,9 map
So currently I am implementing
for (i=0;i<list.size;i++){
map_new.put(list.get(),map.get(list.get()))
}
Is there any efficient way to do this?
Your code basically assumes that map.get(list.get()) always returns a value, you can try the following code which first filters the not null values from the list object and then adds to the new Map:
Map<String, Integer> newMap = list.stream().
filter(key -> (map.get(key) != null)).//filter values not present in Map
collect(Collectors.toMap(t -> t, t -> map.get(t)));//now collect to a new Map
In case, if map.get(list.get()) returns null, your code creates a Map with null values in it for which you might end up doing null checks, which is not good, rather you can ensure that your newly created Map always contains a value for each key.
Assuming the signature of list and the map are as following
List<Integer> list;
Map<Integer, Integer> map;
You can use following
for(int a : list){
Integer b = map.get(a);
if(b != null)
// b is your desired value you can store in another collection
}
Which is similar to the procedure you have already used.
As you can access the map in O(1) so the complexity of this code will be O(listsize)
There is not much you can do for efficiency. Still couple of small things you can do considering code example you have given above:
1) Change your for loop to
for(Long num : list)
instead of iterating using index, this will reduce you get calls over list.
2) You can update the existing map , so that you even do not need to iterate.
map.keySet().retainAll(list);
for(Long key: map.keySet()) {
System.out.println(map.get(key));
}
With this existing map will contain only those data whose keys are present in list, but you should use it carefully depending upon rest of the code logic.
You can capitalize on the fact that the keyset of a map is backed by the map itself and modifications to the keyset will reflect back to the map itself. This way, you can use the retainAll() method of the Set interface to reduce the map with a single line of code. Here is an example:
final Map<Integer, String> m = new HashMap<Integer, String>();
m.put(1, "A");
m.put(2, "B");
m.put(3, "C");
m.put(4, "D");
m.put(5, "E");
final List<Integer> al = Arrays.asList(new Integer[] { 2, 4, 5 });
System.out.println(m);
m.keySet().retainAll(al);
System.out.println(m);
This will output:
{1=A, 2=B, 3=C, 4=D, 5=E}
{2=B, 4=D, 5=E}
I'm trying to use HashMap and Hastable with a List of Object as Key.
Please see below a simplified version of my code which doesn't work.
When I debug this code, I expect having 3 items in the TestMap4 Object but there is only 1.
List<String> lst = new ArrayList<>();
lst.add("Hello");
lst.add("World");
Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
testMap4.put(lst, 1);
testMap4.put(lst, 2);
testMap4.put(lst, 5);
What happens when I put a new item into the HashMap object ? why doesn't it work ?
I obtain the same result with this new example below. (Each List countains the same 2 String)
List<String> lst = new ArrayList<>();
lst.add("Hello");
lst.add("World");
List<String> lst2 = new ArrayList<>();
lst2.add("Hello");
lst2.add("World");
List<String> lst3 = new ArrayList<>();
lst3.add("Hello");
lst3.add("World");
Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
testMap4.put(lst,1);
testMap4.put(lst2,2);
testMap4.put(lst3,5);
If I modify only 1 char of the 2 String, this is OK
You do not understand the concept of HashMap.
Your problem is that you are using the same key each time.
testMap4.put(lst, 1); // <----same key, different value
testMap4.put(lst, 2); // <----same key, different value
testMap4.put(lst, 5); // <----same key, different value
In Hashmap, every value that is stored in the Hashmap, there's a key that is saved with that particular value and is unique for each value stored in Hashmap
Important points about HashMap:
1- A HashMap contains values based on the key.
2- It contains only unique elements.
3- It may have one null key and multiple null values.
4- It maintains no order.
Example
HashMap<Integer,String> hm = new HashMap<>();
Secondarily, using a mutable object (a List<>) as the key results in undefined behavior if any of the lists are modified after they are inserted into the map. The hash code is calculated according to the contract for List (see the Javadoc) only when the entry is first inserted into the map. A change to the list's contents will change the hash code and you will no longer be able to find the entry.
Using a List<> (or any mutable object) as the key in a HashMap<> is a Really Bad Idea™.
It is not working because you are using same key each time for storing different value due to which all the value are getting map to same key and hashmap is only storing the last value since this value override the previous values.
HashMap calls the hashCode() method on the key-object you put in the HashMap.
As you don't have overridden it for the key class you use (List<> in your case) it calls the hashCode() method on java.lang.Objectwhich returns a unique object id.
As you put the same object three times into the Map, it is the same key you put in three times in a row.
List<String> lst1 = new ArrayList<>();
lst.add("Hello");
lst.add("World");
List<String> lst2 = new ArrayList<>();
List<String> lst3 = new ArrayList<>();
Map<List<String>, Integer> testMap4 = new HashMap<List<String>, Integer>();
testMap4.put(lst1, 1);
testMap4.put(lst2, 2);
testMap4.put(lst3, 5);
will give you three entries in your Map.
If you need a HashCode over the contents of the list for usage as HashMap keys, have a look at:
static int java.util.Objects.hash(Object... values)
static boolean java.util.Arrays.equals(Object[] a, Object[] a2)
Don't forget that you always have to override both methods. hashCode() and equals(). You will instantly drop dead, if you only override one of them!! ;)
class MyObject {
int field;
public void setField(int arg1) {
this.field = arg1;
}
}
HashMap<String, MyObject> map;
...
... // put some MyObjects in the map with strings as keys
...
for (MyObject object : map.values()) {
object.setField(12345);
}
The changes I made to objects within the cycle are made on the same objects in the map?
The guide says this about the values() method
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
Does "changes to the map" mean "changes to the mapped objects"? So this way the setField method can change the objects in the map?
Does "changes to the map" mean "changes to the mapped objects"?
It means changes to the map (but see also 1 below). The collection is a live view of the values in the map, so as you add entries to the map or remove entries from the map, the collection reflects those changes; the two are linked. E.g.:
Map<String, String> m = new HashMap<String, String>();
Collection<String> c = m.values();
m.put("hi, "there");
System.out.println(c.size()); // 1, not 0
Live Example
1 Separately: Naturally changes to the state of objects stored as values in the map will be visible regardless of whether you get the reference to those objects via the collection or the map; they're references to the objects, not copies of the objects.
The method HashMap.values() - as described in the javadoc.
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. If the map is modified while an iteration over the collection is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.
What this is saying is it returns a collection (similar to a List) of all the elements in the array. It also states that the collection is backed by the map, so if you change the map, the collection will also update, and changing the collection will also change the map. Note that it is impossible to add elements from this collection.
This example shows the use of the method quite well.
public static void main(String[] args) {
Map<String, String> mapValues = new HashMap<>();
mapValues.put("Hi", "Hello");
mapValues.put("Bye", "Goodbye");
System.out.println(mapValues.size());//prints 2
Collection<String> values = mapValues.values();
values.remove("Hello");
System.out.println(mapValues.size());//prints 1
System.out.println(values.size());//prints 1
mapValues.put("Morning", "Good morning");
System.out.println(mapValues.size());//prints 2
System.out.println(values.size());//prints 2
}
The values method returns a set of references to your objects in memory. Since your objects are mutable, any changes made to them will be reflected in the map, since the map has references to the same memory.
An example for Map that contains:
"key1" -> "value1"
"key2" -> "value2"
values() will return a collection of: "value1", "value2"
And yes, if you use a mutating method on an object in the map it will change in the values() collection that you retrieved previously.
But the most interesting part is that adding/removing elements from map will cause the values collection to change.
Example:
Map<String, String> map = new HashMap<>();
Collection<String> vals = map.values();
System.out.println("Before: " + vals);
map.put("key1", "value1");
System.out.println("After: " + vals);
This will print:
Before: []
After: [value1]
Is there anyone who knows how to convert a Map to a List
I found here something like this :
List<Value> list = new ArrayList<Value>(map.values());
But this is going to store just the value of the Map to the List
What I want to do is : copy the Key & the Value to the List
So, do you know how to achieve this ?
This will give you a List of the Map entries:
List<Map.Entry<Key, Value>> list =
new ArrayList<Map.Entry<Key, Value>>(map.entrySet());
FYI, entries have a getKey() and a getValue() method.
One way you can do is Create a List with adding all the keys like :
List list = new ArrayList(map.keySet());
Then add all the values like :
list.addAll(map.values);
And then probably you have to access with index like:
if map size is 10 , you know that you have 20 elements in the list.
So you have to write a logic to access the key-value from the list with proper calculation of index like: size/2 something like that.
I am not sure if that helps what your requirement is.
Both #Bohemian and #dacwe are right. I'd say moreover: in most cases you do not have to create your own list. Just use map.entrySet(). It returns Set, but Set is just a Collection that allows iterating over its elements. Iterating is enough in 95% of cases.
Try storing the Map.Entrys of the map:
new ArrayList<Entry<Key, Value>>(map.entrySet());
Example:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Hello", 0);
map.put("World!", 1);
ArrayList<Entry<String, Integer>> list =
new ArrayList<Entry<String, Integer>>(map.entrySet());
System.out.println(list.get(0).getKey() + " -> " + list.get(0).getValue());
}
This is a very basic question, I'm just not that good with Java. I have a Map and I want to get a list or something of the keys in sorted order so I can iterate over them.
Use a TreeMap, which is an implementation of the SortedMap interface. It presents its keys in sorted order.
Map<String, Object> map = new TreeMap<String, Object>();
/* Add entries to the map in any order. */
...
/* Now, iterate over the map's contents, sorted by key. */
for (Map.Entry<String, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
If you are working with another Map implementation that isn't sorted as you like, you can pass it to the constructor of TreeMap to create a new map with sorted keys.
void process(Map<String, Object> original) {
Map<String, Object> copy = new TreeMap<String, Object>(original);
/* Now use "copy", which will have keys in sorted order. */
...
}
A TreeMap works with any type of key that implements the Comparable interface, putting them in their "natural" order. For keys that aren't Comparable, or whose natural ordering isn't what you need, you can implement your own Comparator and specify that in the constructor.
You have several options. Listed in order of preference:
Use a SortedMap:
SortedMap<whatever> myNewMap = new TreeMap<whatever>(myOldMap);
This is vastly preferable if you want to iterate more than once. It keeps the keys sorted so you don't have to sort them before iterating.
There is no #2.
There is no #3, either.
SortedSet<whatever> keys = new TreeSet<whatever>(myMap.keySet());
List<whatever> keys = new ArrayList<whatever>(myMap.keySet());
Collections.sort(keys);
The last two will get you what you want, but should only be used if you only want to iterate once and then forget the whole thing.
You can create a sorted collection when iterating but it make more sense to have a sorted map in the first place. (As has already been suggested)
All the same, here is how you do it.
Map<String, Object> map;
for(String key: new TreeSet<String>(map.keySet()) {
// accessed in sorted order.
}
Apart from the methods mentioned in other answers, with Java 8 streams, another shorthand to get a sorted key list from a map would be -
List<T> sortedKeys = myMap.keySet().stream().sorted().collect(Collectors.toList());
One could actually get stuff done after .sorted() as well (like using a .map(...) or a .forEach(...)), instead of collecting it in the list and then iterating over the list.