Why local value is changing after updating hashmap [duplicate] - java

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 1 year ago.
I don't understand why after getting value from hashmap and updating hashmap, the local value changing at updated value. I always thought that java works on pass by value not by reference.
#Component
#RequiredArgsConstructor
public class ParametersCompare {
#NonNull
private final ParameterRepository parameterRepository;
public boolean isAnyChange(String object, List<Parameter> currentParameter) {
Map<String, String> parameterHistory = parameterRepository.getHistoricalParameter(object);
parameterRepository.updateParameters(currentParameter, object);
return isAnyChange(parameterHistory, currentParameter);
}
#Service
public class ParameterRepository {
private final Map<String, Map<String, String>> oldParameters = new TreeMap<>();
public void updateParameters(List<Parameter> currentParameters, String object) {
Map<String, String> oldParameters = this.oldParameters.computeIfAbsent(object, s -> new HashMap<>());
updateParameters(currentParameters, oldParameters, object);
}
public Map<String, String> getHistoricalParameter(String object) {
Map<String, String> currentParameters = this.oldParameters.get(object);
if (object == null) {
return Collections.emptyMap();
} else {
return currentParameters;
}
}
private void updateParameters(List<Parameter> currentParameters, Map<String, String> oldParameters, String object) {
currentParameters.forEach(parameter -> oldParameters.put(parameter.getName(), parameter.getValue()));
this.oldParameters.put(object, oldParameters);
}
}
After line
parameterRepository.updateParameters(currentParameter, object);
oldParameters is changing to variable received from currentParameter.
Thanks in advance for pointing why is changing.
best regards

Java is pass-by-value on all fronts, yes, but remember that all non-primitive values are references. For non-primitives, the values you pass around are always treasure maps and never the treasure. = wipes out the old 'X marks the spot' and draws a new X in, and . (as well as [] and a few others, like synchronized(ref)) are java-ese for `follow the X, find a shovel, dig down, and operate on the treasure you find'.
List<String> hello = new ArrayList<String>();
conjures up a new treasure chest out of thin air. Bury it someplace on the vast beach. Like all objects, it has no name.
conjure a treasure map out of new air. It is named hello.
Draw an X on the hello map, marking the position where you buried the chest you made in X.
foo(hello);
Make a copy of your hello map, and hand the copy of the map to foo. If foo reassigns their map (they do param = somethingElse;), your map does not change, as they are operating on their copy. If, however, they follow their map and dig down, well, they find the same treasure you would find on your map:
List<String> list = new ArrayList<String>();
op1(list);
System.out.println(list);
public void op1(List<String> list) {
list.add("HELLO");
}
public void op2(List<String> list) {
list = List.of("HELLO");
}
In this code, it prints HELLO, because op1 follow its map and modified the treasure. If you replace it with a call to op2, nothing is printed; op2 makes new treasure and updates its own map which you will not observe, as java is indeed pass-by-value.
so, how can I use variable from map as value not as reference?
Given that variables are references, you are doing it. Presumably you want: "How do I ensure that the method I hand this map to gets its own clone?" and the answer is pretty much in that restated question: By cloning. There is nothing baked into java, because objects need to represent data concepts that are cloneable in the first place. For example, you can't feasibly clone any InputStream representing an underlying (OS-level) file handle. Also, cloning a huge array or any other object with a large internal data store would then be extremely expensive. Furthermore, any object that has a field of such a non-cloneable type would itself then also be non-cloneable, so non-cloneables are all over the place, and that perhaps explains why java has no baked in support for it.
Most data types where you'd want to make clones DO however have APIs that let you do that:
List<String> original = new ArrayList<String>();
original.add(...);
List<String> clone = new ArrayList<String>(original);
Now clone is a truly full clone. It's a shallow copy, which in this case is irrelevant, as String is an immutable datatype. That means the treasure chest is made from solid titanium - nobody can mess with it or move it, so you can hand out copies of a map that leads to it with wild abandon - it will never effect you. That's why immutable data types are often quite convenient. No worries about cloning and handing out references (=treasure maps).

Related

Two HashMaps "sharing" the same data?

I have a problem, probably because of my inexperience about HashMaps. Basically, I have a class that contains two HashMap variables:
private Map <String, List<String>> definiciones = new <String, List<String>> HashMap();
private Map <String, List<String>> sinonimos = new <String, List<String>> HashMap();
They store Strings in their Lists when I call their respective functions. The problem is that, for example, when I add a String to the List inside definiciones Map, it also appears somehow in sinonimos.
public void agregarDefinicionAPalabra(String palabra, String definicion) throws PalabraInvalida {
System.out.println(definiciones.get(palabra).size());
if(definicion.equals("")) {
JOptionPane.showMessageDialog(null, "La definición no puede estar vacía");
throw new PalabraInvalida("La definición no puede estar vacía");
}
if (definiciones.containsKey(palabra)) {
definiciones.get(palabra).add(definicion);
} else {
throw new PalabraInvalida("No se ha encontrado la palabra solicitada");
}
System.out.println(sinonimos.get(palabra).get(0));
}
The method adds a String to the definition List, as you can see, in the end I put a print to confirm this idea that I just wrote. Indeed, when I run this method, the sinonimos List gets the definiciones element that I added.
Obviously there is something that I don´t know due to my inexperience in this topic, any suggestions?
Thank you!
You probably put the same list object into both of your HashMaps. In Java, all objects are referenced by their memory address. When you put a list into your HashMap, it's really just storing the memory address of the list.
As an analogy, if I gave the street address of my home to two friends, and both of them wrote down my address, I don't suddenly get two houses. If one of my friends leaves a package on my doorstep, and then the other friend comes by, they will see the package still there.
So, in the code that is creating the Lists, make sure you create distinct, separate lists for the two Maps.
I think you must be using the same List object for the value in the definiciones and sinonimos maps. For example, you must be doing something like:
String palabara = "example";
List<String> list = new ArrayList<>();
definiciones.set(palabra, list);
sinonimos.set(palabra, list);
What happens here is that you now have the same list as the value in both maps. So when you call definiciones.get(palabra) and sinonimos.get(palabra), both of these will give you references to the same list in memory.
What you need to do instead is:
String palabara = "example";
definiciones.set(palabra, new ArrayList<>());
sinonimos.set(palabra, new ArrayList<>());
Diagram
The other two Answers are correct. I'll add this graphic to make visual the problem and solution.
Copy the list
Or if you want the second list to be based on contents of the first, make a copy. To make a copy, feed the first list to the constructor of the second list.
sinonimos.set( palabra , new ArrayList<>( firstArrayList ) ) ;
An entirely new and separate list is created. But the elements in both lists point to the same content objects. So be aware of those element objects being mutable or immutable.

Java - Initialize a HashMap of HashMaps

I am new to java and practicing by creating a simplistic NaiveBayes classifier. I am still new to object instantiation, and wonder what to do to initialize a HashMap of HashMaps. When inserting new observations into the classifier, I can create a new HashMap for an unseen feature name in a given class, but do I need to initialize?
import java.util.HashMap;
public class NaiveBayes {
private HashMap<String, Integer> class_counts;
private HashMap<String, HashMap<String, Integer>> class_feature_counts;
public NaiveBayes() {
class_counts = new HashMap<String, Integer>();
// do I need to initialize class_feature_counts?
}
public void insert() {
// todo
// I think I can create new hashmaps on the fly here for class_feature_counts
}
public String classify() {
// stub
return "";
}
// Naive Scoring:
// p( c | f_1, ... f_n) =~ p(c) * p(f_1|c) ... * p(f_n|c)
private double get_score(String category, HashMap features) {
// stub
return 0.0;
}
public static void main(String[] args) {
NaiveBayes bayes = new NaiveBayes();
// todo
}
}
Note this question is not specific to Naive Bayes classifiers, just thought I would provide some context.
Yes, you need to initialize it.
class_feature_counts = new HashMap<String, HashMap<String, Integer>>();
When you want to add a value to class_feature_counts, you need to instantiate it too:
HashMap<String, Integer> val = new HashMap<String, Integer>();
// Do what you want to do with val
class_feature_counts.put("myKey", val);
Recursive generic data structures, like maps of maps, while not an outright bad idea, are often indicative of something you could refactor - the inner map often could be a first order object (with named fields or an internal map), rather than simply a map. You'll still have to initialize these inner objects, but it often is a much cleaner, clearer way to develop.
For instance, if you have a Map<A,Map<B,C>> you're often really storing a map of A to Thing, but the way Thing is being stored is coincidentally a map. You'll often find it cleaner and easier to hide the fact that Thing is a map, and instead store a mapping of Map<A,Thing> where thing is defined as:
public class Thing {
// Map is guaranteed to be initialized if a Thing exists
private Map<B,C> data = new Map<B,C>();
// operations on data, like get and put
// now can have sanity checks you couldn't enforce when the map was public
}
Also, look into Guava's Mulitmap/Multiset utilities, they're very useful for cases like this, in particular they do the inner-object initializations automatically. Of note for your case, just about any time you implement Map<E, Integer> you really want a Guava Multiset. Cleaner and clearer.
You must create an object before using it via a reference variable. It doesn't matter how complex that object is. You aren't required to initialize it in the constructor, although that is the most common case. Depending on your needs, you might want to use "lazy initialization" instead.
Do not declare your variables with HashMap. It's too limiting.
Yes, you need to initialize class_feature_counts. You'll be adding entries to it, so it has to be a valid map. In fact, initialize both at declaration and not in the constructor since there is only one way for each to start. I hope you're using Java 7 by now; it's simpler this way.
private Map< String, Integer> classCounts = new HashMap<>();
private Map< String, Map< String, Integer>> classFeatureCounts = new HashMap<>();
The compiler will deduce the types from the <>. Also, I changed the variable names to standard Java camel-case style. Are classCounts and classFeatureCounts connected?

Filling Lists with Default Values from final static Lists

The title is probably not the best, I apologize for that.
I have several final static Lists I am using to define defaults for database values. The default list of values should never change, as such when populating them, I use Collections.nCopies(int,T) to obtain an immutable List. These Lists are then used to populate lists in another class with defaults. The values in these Lists are expected to change.
Pseudocode for the class of defaults:
public final class FooDefaults {
public final static List<Integer> LIST_ONE;
public final static List<String> LIST_TWO;
//This map allows easier access to "column" values.
public final static List<Map<String,String>> LIST_THREE;
static {
LIST_ONE = Collections.nCopies(7, 5);
LIST_TWO = Collections.nCopies(10, "boo");
Map<String, String> temp = new java.util.LinkedHashMap<>();
for(int i=0;i<15;i++) {
temp.put(("Param"+i),"foo");
}
LIST_THREE = Collections.nCopies(10, temp);
}
}
Pseudocode for the class of editable values:
public class Foo {
//Keep the reference from changing.
//Prevents an accidental new.
private final List<Integer> listOne;
private final List<String> listTwo;
private final List<Map<String,String>> listThree;
public Foo() {
listOne = new java.util.ArrayList<>(FooDefaults.listOne);
listTwo = new java.util.ArrayList<>(FooDefaults.listTwo);
listThree = new java.util.ArrayList<>(FooDefaults.listThree);
}
}
My concern is that as I have performed a shallow copy on these lists, changes in the lists in Foo, will be visible in the Lists in FooDefaults.
This post: https://stackoverflow.com/a/1685158/1391956 suggests that due to Strings and Integers being immutable, I need not worry about accidentally overwriting the values in FooDefaults.LIST_ONE and FooDefaults.LIST_TWO.
Thus, my primary concern are the values contained within the maps in FooDefaults.LIST_THREE. If I change the values in the maps in Foo's listThree, will the change be visible in FooDefaults?
If so, what would be the most efficient way to handle this? Class Foo is likely to be instantiated over a thousand times and added to a List in another class, thus speed will potentially be an issue.
I originally created the final static lists in FooDefaults in the interest of speed, as it is my (probably incorrect) assumption that creating those Lists in FooDefaults and simply copying the data would be faster than creating them every time Foo is instantiated.
EDIT: If I must perform a Deep Copy I plan on using something similar to:
public static final List<Map<String, String>> getListThreeCopy() {
Map<String,String> temp = new java.util.LinkedHashMap<>();
for(Map.Entry<String, String> entry: LIST_THREE.get(0).entrySet()) {
temp.put(entry.getKey(),entry.getValue());
}
List<Map<String,String>> rtnList = new java.util.ArrayList<>();
for(int i=0;i<LIST_THREE.size();i++) {
rtnList.add(temp);
}
return rtnList;
}
Would there be a faster way?
In the end, I used the code mentioned at the end of my post in a loop to perform a Deep Copy 1,500 times and timed it. I did the same with the instantiation code.
As I somewhat expected, recreating the List from scratch is much faster than a deep copy.
16 milliseconds with the Deep Copy versus 0 milliseconds with instantiation. I timed it using System.currentTimeMillis().
So long as I only create it in a static function, I have little reason to worry about errors.

What's the best way to detach a Collection from a Map in Java?

I obtain a HashSet from a HashMap and I don't want that my modifications on the HashSet reflect on the HashMap values.
What's the best way of doing something like this :
HashSet<Object> hashset = new HashSet((Collection<Object>) hashmap.values());
//Something like ...
hashset.detach();
//Then i can modify the HashSet without modifying the HashMap values
Edit :
I have to modify an element in the HashSet but I don't want to modify this same element in the HashMap.
Thanks!!!
If you're creating a new HashSet as per the first line of your code snippet, that's already a separate collection. Adding or removing items from the set won't change your hashMap. Modifying the existing items will, of course - but that's a different matter, and will almost always be a Very Bad Thing (assuming your modifications affect object equality).
When you create the HashSet from hashMap.values() like this, then it's already "detached" in the sense that modifying the HashSet will not influence the map it was constructed from.
However, if you modify an object inside the set (for example calling a setter on it), then those changes will be reflected inside the HashMap as well (since the Set and the Map will refer to the same object).
One way around this is to make defensive copies of each element (using clone() or by using a copy constructor).
Another way is to use immutable objects.
You are close:
Set<Object> set = hashmap.values(); // is backed by the map
// create a new hashset seeded from the other set
Set<Object> hashset = new HashSet<Object>(set);
If you are trying to copy the values, and change the state of the values you need to create a deep copy, which relies on knowing how to create copies of the objects held in the Map as values. Hopefuly this test illustrates what I mean.
#Test
public void testHashMap() throws Exception {
final Map<Integer, TestContainer<Double>> hashmap = new HashMap<Integer, TestContainer<Double>>();
final TestContainer<Double> t1 = new TestContainer<Double>(1d);
final TestContainer<Double> t2 = new TestContainer<Double>(2d);
hashmap.put(1, t1);
hashmap.put(2, t2);
// create a separate collection which can be modified
final Set<TestContainer<Double>> hashset = new HashSet<TestContainer<Double>>(hashmap.values());
assertEquals(2, hashmap.size());
assertEquals(2, hashset.size());
hashset.remove(t2);
assertEquals(2, hashmap.size());
assertEquals(1, hashset.size());
// prove that we cannot modify the contents of the collection
hashset.iterator().next().o += 1;
assertEquals(2d, t1.o, 0d);
}
private static final class TestContainer<T> {
private T o;
private TestContainer(final T o) {
this.o = o;
}
}
Try this:
public MyType cloneObject(MyType o) {
MyType clone = new MyType();
// TODO copy the attributes of 'o' to 'clone' return the clone
return clone;
}
public void populateHashSet(HashMap<Object,MyType> hashMap) {
HashSet<MyType> hashSet = new HashSet<MyType>();
for (MyType o : hashMap.values()) {
hashSet.add(cloneObject(o));
}
}
That said, I would be very careful about making copies of objects unless all the attributes of the object are primitive/immutable types. If you just copy an attribute object reference to an object reference in the clone then your 'clone' can still produce side-effects in the original object by changing the objects it references.

Changing value after it's placed in HashMap changes what's inside HashMap?

If I create a new HashMap and a new List, and then place the List inside the Hashmap with some arbitrary key and then later call List.clear() will it affect what I've placed inside the HashMap?
The deeper question here being: When I add something to a HashMap, is a new object copied and placed or is a reference to the original object placed?
Thanks!
What's happening here is that you're placing a pointer to a list in the hashmap, not the list itself.
When you define
List<SomeType> list;
you're defining a pointer to a list, not a list itself.
When you do
map.put(somekey, list);
you're just storing a copy of the pointer, not the list.
If, somewhere else, you follow that pointer and modify the object at its end, anyone holding that pointer will still be referencing the same, modified object.
Please see http://javadude.com/articles/passbyvalue.htm for details on pass-by-value in Java.
Java is pass-by-reference-by-value.
Adding the list to the hash map simply adds the reference to hash map, which points to the same list. Therefore, clearing the list directly will indeed clear the list you're referencing in the hashmap.
When I add something to a HashMap, is
a new object copied and placed or is a
reference to the original object
placed?
It is always a reference to the object.
If you clear the HashMap the object will be still "live".
Then the object will be destroyed by the garbage collector if no one is referencing it anymore.
If you need to copy it, take a look to Object.clone() method and to the Cloneable interface
Map<Integer, Integer> hasmapA = new HashMap<>();
hasmapA.put("key1", "value1");
hasmapA.put("key2", "value2");
hasmapA.put("key3", "value3");
Copy By reference: If you assign one HashMap to other then both point to same reference in memory.
Map hasmapB;
hashmapB = hashmapA;
If you make changes in any of these, changes will reflect in both HashMap as both are referencing to same location.
Copy By Value: If you wan to
clone/deepcopy/create separate memory location/create separate object
of hashmapB while copying content of hashmapA
Map<Integer, Integer> hashmapB = new HashMap<>();;
hashmapB.putAll(hashmapA)
Note: **
Have you noticed difference in both points for hashmapB declaration? In second point we have to call **HashMap constructor. So that we can putAll data of hashmapA into hashmapB.
Reference
Generally you always deal with references in Java (unless you explicitly create a new object yourself with "new" [1]).
Hence it is a reference and not a full object copy you have stored in the map, and changing the list will also effect what you see when going through the map.
It's a feature, not a bug :)
[1] Puritans will include "clone()" and serialization, but for most java code "new" is the way to get objects.
Try it out
package test32;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
class Foo {
public Foo(int id, String name) {
this.id=id;
this.name=name;
}
public static void main(String[] args) {
HashMap strs = new HashMap();
// create a list of objects
List ls = new ArrayList();
ls.add(new Foo(1, "Stavros"));
ls.add(new Foo(2, "Makis"));
ls.add(new Foo(3, "Teo"));
ls.add(new Foo(4, "Jim"));
// copy references of objects from list to hashmap
strs.put("1", ls.get(0));
strs.put("2", ls.get(1));
strs.put("3", ls.get(2));
strs.put("4", ls.get(3));
System.out.println("list before change : " + ls);
System.out.println("map before change: " + strs);
// get an object from the hashmap
Foo f=strs.get("1");
// set a different value
f.setId(5);
// observe that the differences are reflected to the list and to the hashmap also
System.out.println("list after change : "+ls);
System.out.println("map after change: "+strs);
}
private int id;
public void setId(int id) {
this.id=id;
}
public int getId() {
return this.id;
}
private String name;
public void setName(String name) {
this.name=name;
}
public String getName() {
return this.name;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(id);
sb.append("-");
sb.append(name);
return sb.toString();
}
}
The Correct answer for this is Explained below :
Suppose you have a HashMap called hashmap and initially you put a key value pair in this HashMap e.g. hashmap<"test1","test2">.
After this when you pass this hashmap to a function where you again changes its value to test3 like hashmap.put("test1", "test3") and print the map again in main Method, the Java Pass by Value Concept fails here.
The Reason is :
When you use HashMap, it does the Hashing for the Key(test1) and store the value. When you passed it to the function where it again changes its value, it again does the hashing for the same key and gets the same memory Address and changes the value Accordingly.
Thats why when you try to retreive the key "test1" it gives you the result as "test3"
Geeze people...everything in Java is pass by value. When you pass an object, the value you are passing is the object reference. Specifically, you are passing a copy of the object reference. There are no pointers in Java either, though references are similar. Get it right!

Categories

Resources