Do I need to remove value and then add or can I directly add it in HashMap and it will efficiently updated ?
i.e.
HashMap<String, String> person = new HashMap<String, String>();
person.add("name", "John");
now which would be a better/efficient way to update above key value :
a)
person.remove("name");
person.add("name", "jamy");
b)
person.add("name", "jamy");
or are both just the same ?
This is a very small example. Considering a large HashMap containing other HashMaps, which would be better option and is there any even more efficient way to do so ?
You can put the new value. It will substitute the old one.
From javadoc:
If the map previously contained a mapping for the key, the old value is replaced.
Note that the method is put, not add. add is not a method of Hashmap
Edited: I added the reference link to the documentation as Naman Gala commented.
Use put method, as it will replace value for given key if key exist , otherwise it will create new entry
java.util.HashMap provides put method to add key and object pair in the map. You do not have to remove the object if you want to update the HashMap object. It will return the previous object if there is any object in the map with the same key. Otherwise, it will simply return null. You do not have to remove it every time. Just use
java.util.HashMap.put(key, object);
Related
I am working on a project that involves minor combining and relating of datasets for some work, and have been stuck for some time.
I have groups of data, which are similar to other groups of data in a project. I have an array list of the group's names. I am comparing these similar groups to other dataset, describing similar things. (A collection of groups, which are similar to other groups in their own collection).
I've been trying to solve this by using:
HashMap<ArrayList<String>, ArrayList<String>>
It is proving very difficult to add another group (by name, a String) when another relation is found.
If I find another group from each dataset and want to add to a current ArrayList (which is why I am using ArrayLists), it creates another entry, where the new key and value are the same as the previous but with the added element in each ArrayList.
Here is the current, relevant code:
...
for(ArrayList<String> similarGroupsDataset : map.keySet()) {
...
ArrayList<String> value = map.get(similarGroupsDataset);
ArrayList<String> key = similarGroups;
value.add(groupToAdd);
key.add(groupToAdd2);
map.remove(similarGroupsDataset);
map.put(key, value);
}
Store the ArrayList key and Arraylist value into variables, add the newly found pieces of data, remove the old entry, and add the updated version.
For some reason this seams to not remove the entry which does not have the newly added found data.
So if I print out the map, it would look like
({1,2},{a,b}) , ({1,2,3},{a,b,c})
What it should look like is
({1,2,3} , {a,b,c}), taking out the irrelevant entry.
Where 1,2 in dataset1 are similar, which are similar still to a,b from dataset2, etc. if that makes sense.
I have tried to do
map.get(relevantGroupFromDataset2).add(data)
//adds the newly found similar group to the list of groups
//which are all similar to eachother, from dataset1.
That works sometimes, but only for the value, not the key, it seems.
In the end, my goal is to remake these datasets with an identifier tying these groups together by a new identifier, rather than their current identifier which doesn't tie them together in the way I want.
Am I doing something wrong here? Is there a better data structure to use in this scenario? Is a HashMap or similar structure the way to go?
If I find another group from each dataset and want to add to a current
ArrayList (which is why I am using ArrayLists), it creates another
entry, where the new key and value are the same as the previous but
with the added element in each ArrayList.
You use as key an ArrayList.
In a map, the keys are retrieved from their hashCode()/equals() method.
So when you change the content of the ArrayList key here :
ArrayList<String> value = map.get(similarGroupsDataset);
ArrayList<String> key = similarGroups;
value.add(groupToAdd);
key.add(groupToAdd2); // <-- here
hashCode() and equals() will not produce the same result any longer.
It is considered as a new key for the map.
So map.put(key, value); will add a new element.
It is not obvious to give you a good workaround with your actual code as the logic performed by the Map and what you expect is really not clear.
For example :
value.add(groupToAdd);
key.add(groupToAdd2);
is either a very bad naming or you populate only with groups the key-values of your map.
The general idea is you should not use in your map a key which the hashCode()/ equals() result may change after that the key were added in the map.
To achieve it :
Either put the value with the the ArrayList key at a time where you know that the key will not be modified any longer.
Either remove the value with the key and add it again with the new key.
In any case, to avoid this kind of errors, you should create an unmodifiable List for keys and pass it in the map:
map.put(Collections.unmodifiableList(keys), ...);
In a Map one should better keep the key object immutable.
When one changes the key object in a HashMap, and the new hashCode is different, the map is corrupt.
So you have to remove the old key object and insert the new key object.
The data structure fitting your example would be a tree of (group, datum), where you extend the path to the leafs.
tree -> (a, 1)
+--> (x, 24)
+--> (b, 2)
+--> (c, 3)
And consider only all paths to a final leaf.
Admittedly a bit more work.
By key.add(groupToAdd2); the key is changed, but keys of a map must be effective immutable:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed...
Swap the lines key.add(groupToAdd2); and map.remove(similarGroupsDataset); to fix this or even better:
...
for (Entry<ArrayList<String>, ArrayList<String>> entry : map.entrySet()) {
...
map.remove(entry.getKey()); // remove from map before changing the key
entry.getKey().add(groupToAdd2);
entry.getValue().add(groupToAdd);
map.put(entry.getKey(), entry.getValue());
}
I have something along the lines of this:
public HashMap<Boolean, String> map = new HashMap();
map.put(this.inverted, "Inverted");
map.put(this.active, "Loading");
System.out.println(map.size());
after seeing that the size was always 1, I realised that using map.put was overriding the previous data. I am currently trying to iterate over the hashmap. Is there a way to add mappings to it without overriding previous ones?
You have declared your HashMap as: -
public HashMap<Boolean, String> map = new HashMap();
Now, just think how many maximum mapping can you have in your map? The answer you can get by thinking of, what all values can your Boolean type take. This is because, you cannot have duplicate keys in a HashMap.
So, probably you got it now, that you can at max have only 2 mappings in your map, one for true and other for false(In fact you can have a 3rd one too, as you can have a mapping for a null key too in your HashMap).
So, in your case, if both this.inverted and this.active are either true or false. Then only one of them can be there, and that would be the later value inserted.
Is there a way to add mappings to it without overriding previous ones?
Probably you have build your HashMap wrongly. You should declare your map as: -
private Map<String, Boolean> map = new HashMap();
And now you can put two mappings as: -
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
It's because this.inverted.equals(this.active) and this.inverted.hashcode()==this.active.hashcode()
Maybe you need redefine the equals method for the key.
In MAP
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. ---> from Map Api
from your implementation, may be this.inverted and this.active both have same value.
Check the input once. print the keySet, then check.
or change the input to Map<String, Boolean>
As #Frank suggest you should invert your Map.
public final Map<String, Boolean> map = new HashMap<>();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map);
If the keys are the same than the previous value is overwritten in a standard Java Map. If you don't want this, you can have a look at a multimap which is implemented for example in commons-collections. It can hold different values for one key.
Hashmap is based on key/value pairs. If your keys are equal (they have the same hashcode), it will behave as you described.
For your use case, reversing your key/value pairs will help you.
public HashMap<String, Boolean> map = new HashMap();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map.size());
Get object of innermap ,by passing outer map key .. Then check if key of innermap exists then update values with previous data. else create new object of inner map.
I have a Map like this :
Map<String,GridCell> cellsMap
I pass this into a method and the return from that method should contain a Map(say answerMap) which contains all the entries of cellsMap map plus an extra entry that contains a String as the key and a String as the value . Something like :
Map<String,Object> answerMap = new ConcurrentHashMap<String,Object>();
//answer should first contain all the map entries of cellsMap and then add an extra entry like the following
answer.put(getId(), getSelectionValue()); // getSelectionValue() returns a String that contains coordinates of the selected cells.
return answerMap;
Have you considered the Map.putAll() method ?
e.g.
answerMap.putAll(cellsMap);
I don't think this is a good object model, by the way. I think you're better off creating a new class that contains your original map (maybe a copy) and an additional field for your String/String pair.
Otherwise you're throwing objects of different types into the same map, and that's going to make life complicated when you later extract that info. Each time you extract via a key you're going to have to check the type of the object returned. Note that ConcurrentHashMaps don't maintain insertion order.
Use clone() method.
HashMap answerMap = (HashMap)cellsMap.clone();
Map interface has putall() method which add all values of another object in Map.
I have a need to store a list of dynamically created objects in a way where they can all be retrieved and their methods called on demand.
As far as I can see for the list and creation, a HashMap fits my needs but i'm a bit puzzled on recalling the objects and calling their methods using the HashMap.
Just as a reference, let me give you a little code:
Here is the HashMap:
Map<String, Object> unitMap = new HashMap<String, Object>();
// here is how I put an object in the Map notice i'm passing coordinates to the constructor:
unitMap.put("1", new Worker(240, 240));
unitMap.put("2", new Worker(240, 240));
Now I need to create a method that retrieves every object in the hashmap and call a method from each object. is this possible or can the created objects only be referenced directly. If so, is there another way to call a method of all existing instances of a class dynamically (in other words, on user input)?
Sure. You can do this:
for (Object thing : unitMap.values()) {
// use "thing" here
}
If you need the keys too, you can either get just the keys:
for (String key : unitMap.keySet()) {
// use "key" here
}
or both the keys and values together:
for (Map.Entry<String, Object> entry : unitMap.entrySet()) {
// use "entry.getKey()" and "entry.getValue()"
}
In all the above cases, each entry in the map is traversed one by one. So at the end of the loop, you'll have processed all the entries in the map.
If all of the values in the Map are Worker objects, you should declare your map to be of type Map<String, Worker>. This way, when you pull a value out of the map, it will be typed as a Worker. This way you can call any method declared on Worker as opposed to having to check the type at runtime using instanceof.
If the map holds different values, and you need to keep the value type as Object, it may be advantageous to use an interface to define the method that you want to call for each different object type.
If you do not know what method you want to run on the values until runtime, and the map can hold different values, you will just have to do what you are currently doing, and use Map<String, Object>.
Finally, to get the values of the map, you do just as Chris Jester-Young mentioned before me. The biggest advantage, as I said previously, is that your objects will be typed, and you will have no need for casting/instanceof checking.
I use this to put all values from hashMap on a List, hope it helps.
private List<String> getValuesFromHashMap(HashMap<String, String> hashMap) {
List<String> values = new ArrayList<String>();
for (String item : hashMap.values()) {
values.add(item);
}
return values;
}
I have a HashMap having key as my own object and key as ArrayList of String. Is there a way to get the key object from the map which is equal to another object without iterating the map. Please note that my object has implemented equals & hashcode. And it only uses 2 attribute of the class to compare. The another object which I am trying to find in the keys of the map has those 2 attribute equal but the other attributes may be different in the key of the map.
//The actual map
private HashMap<FileDetail, ArrayList<String>> map = new HashMap<FileDetail, ArrayList<String>>();
//object to search in above map without iteration.
FileDetail file = some object;
I want to get the reference of the "file" object in the keys of the map.
No you can't do that. HashMap are supposed to work the other way : you have the key, you're looking for the object.
If you have an object and you want to find the key, there's probably something wrong in your logic and your looking in a wrong direction to solve your problem.
If you don't want to iterate over the keySet, then you can use Guava's BiMap. A biMap has an inverse view which is another bimap containing reversed keys and values. This is how you would use it:
BiMap<FileDetail, ArrayList<String>> biMap = HashBiMap.create();
//object to search in above map without iteration.
FileDetail file = some object;
FileDetail key = biMap.inverse().get(biMap.get(file));
If you really need to do this without iteration over the keySet (e.g. because the map is very large), I suggest storing both the key and the list as values in the map. Either create some specific class encapsulating both, or use a simple pair class. The map would look like:
Map<FileDetail, Pair<FileDetail, List<String>>>
If you can't change the type of the map, you can use a second Map<FileDetail, FileDetail> where key and value are always the same objects.
This question is five years old, but I had the same question just today, and found this page. I thought I'd share the solution I decided upon using, which is not described in any of the existing answers and avoids iterating over all the keys in the map. (Please be gentle; this is my first posting on SO. It's tough finding questions I can answer that don't already have answers. Moreover, every question I've had to date has already been asked on SO. I've been using SO for years, with no ability to comment or vote on answers.)
As has been stated already, maps are designed so that when you have a key, you look up a value. That being the case, the answer is to use the key also as the value, so that when you perform a lookup using an arbitrary key, which equals your original key but is not necessarily == to it, you get back the original key. The issue then, is how to get what you originally intended to be the value.
My solution depends on having control of the class used for the key, and control of the map, with the ability to redefine them, which appears to be the case for the OP. In the OP's example, this would be control of the FileDetail class and of the private map variable. Assuming such control, the FileDetail class would be modified to contain a member variable of type ArrayList<String>, which for my sample code below I'll call list, with associated setter and getter methods. For the private map variable, it would be defined thusly:
private HashMap<FileDetail, FileDetail> map = new HashMap<>();
Now, when you want to put a new ArrayList<String> object in the map, assigned to a specific FileDetail key, you assign the ArrayList<String> object to the FileDetail's ArrayList<String> member variable instead, and then place the FileDetail object in the map.
public void putInMap(FileDetail fd, ArrayList<String> al) {
// Ignoring null conditions for simplicity...
fd.setList(al);
map.put(fd, fd);
}
Later, when you get some arbitrary FileDetail object (one that equals the key but isn't necessarily == to it), and you want the associated key, it's a matter of doing a normal lookup:
FileDetail otherFd = getArbitraryFileDetail();
FileDetail originalKeyFd = map.get(otherFd);
And to get the associated ArrayList<String> after having performed the above:
ArrayList<String> al = originalKeyFd.getList();
Certainly this all hinges on the implementations of the equals and hashCode methods of the FileDetail class, but the OP already had those methods defined as desired.
Hope this helps anyone who, like me, comes to this page with a similar situation.
You are likely looking for a Bidirectional Map, Apache Commons Collections includes this as part of the library (im sure there are other imeplementations as well.) A bidirectional map, just as the name implies, is a map but written so as to make looking up by key or by value efficient.
In Java, HashMap associates a key with a value, not the other way around.
You can retrieve a Set of all of the keys using HashMap.keySet(), or alternatively iterate over all of the entries using HashMap.entrySet():
for (Entry <FileDetail, ArrayList<String>> entry : map.entrySet()) {
FileDetail key = entry.getKey();
if (shouldProcess(key)) {
ArrayList<String> list = entry.getValue();
processList(list);
}
}
We get the key object from Hashmap without iterating the keyset of HashMap by converting keyset to ArrayList. This is a simple example:
//Creating hashmap
HashMap<String, String> map = new HashMap<String, String>();
//Adding elements into the map
map.put("1", "Amit");
map.put("2", "Ananth");
map.put("3", "Sunil");
//Get the list from keyset
ArrayList myKeyList = new ArrayList(map.keySet());
//object to search in above map without iteration.
String myobj = "3";
System.out.println(myKeyList.get(myKeyList.indexOf(myobj)));