Two HashMaps "sharing" the same data? - java

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.

Related

Create multiple Lists from one (Group List's Objects by a specific field)

Given that I have the array of :
List<CustEnt> bulkList= CustRepo.fetchData();
//System.out.println(bulkList) -->
gives me :
CustEct(name:"kasis",age:24,surname:"kumar"),CustEct(name:"samika",age:50,surname:"sharma"),CustEct(name:"manoj",age:84surname:"kumar")
OR
bulkList.get(1) --> CustEct(name:"kasis",age:24,surname:"kumar")
I want to create a new array which is grouped by the 3rd parameter of surname object.
So that my array becomes
ArrayFinal = [CustEct(name:"kasis",age:24,surname:"kumar"),CustEct(name:"samika",age:50,surname:"sharma")],CustEct(name:"manoj",age:84surname:"kumar")
So that when we do .get(1) we would get object of kasis and samika.
Need the help in respective to java 8.
I heard that we can use the Map ,but can anyone give the small code sample or any other implementation guide.
A Map tracks key-value pairs.
Your key is the surname string.
Your value is a list of the CustEnt objects carrying that surname.
Map<String, List<CustEnt>>
Modern syntax with streams and lambdas makes for brief code to place your objects in a map.
Something like:
Map<String, List<CustEnt>> map = originalList.stream.collect(Collectors.groupingBy(CustEnt::getSurename));
Map<String, List<CustEntity>> NamesList
= bulkList.stream()
.collect(Collectors.groupingBy(CustEntity::getSurames));
for (Map.Entry<String, List<CustEntity>> entry: NamesList.entrySet()) {
ExcelGenerationService exp = new ExcelGenerationService( entry.getValue());
//service call
exp.export(entry.getKey());
}

Why local value is changing after updating hashmap [duplicate]

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).

Sorting an ArrayList<Hashmap<String,String> by one field

In an Android application I call a web service, get json response and put that into an ArrayList of Hashmap (each identifier returned in the JSON gets a field.
My list which stores [n] items is defined as:
private ArrayList<HashMap<String, String>> customerDataList;
When I receive the data I create a new Hashmap collection like so:
HashMap<String, String> customer = new HashMap<>();
And then I simply add the values I need for example
JSONObject thisCustomer = customerArr.getJSONObject(customerIndex);
String customerName = thisCustomer.getString(TAG_CUSTOMER_NAME);
customer.put(TAG_CUSTOMER_NAME, customerName);
double location = // get location based on GPS
customer.put(TAG_LOCATION, location);
// snip...
Then once I have populated that customer object I add it to the list:
customerDataList.add(customer);
Now, I want to sort this data by one value, the location which I set above.
Currently after I have populated customerDataList I loop through it, build an array of all the locations, sort it, and then loop over the sorted array. Inside that I loop over customerDataList and compare the outer location (the sorted values) with the inner, and build another customerDataList which is sorted.
Is there a way I can sort without needing to have 3 loops?
Edit: Mike does your solution work with doubles?
The unsorted locations are as follows
1513.70
702.59
814.59
604.99
However the final list after I call your class is
1513.70
604.99
702.59
814.59
If I understood the question correctly it sounds like you could use a custom comparator to sort the array list in the first place:
public class LocationComparator
implements Comparator<HashMap<String, String>>
{
#Override
public int compare(HashMap<String, String> o1,
HashMap<String, String> o2)
{
return Double.compare(o1.get(TAG_LOCATION), o2.get(TAG_LOCATION));
}
}
Collections.sort(customerDataList, new LocationComparator());
And of course, you could use an anonymous class to implement the Comparator<> interface inline.
But as an aside, why are you storing your customer entities in hash maps? It seems wasteful..

How To Access hash maps key when the key is an object

I cannot seem to figure out how to access the values of my hashmap
What I am basically trying to do is create a hashmap with an array as one of the values like json style.. If that makes sense?
So I want something like hash{key: value1, value2, value3, [number1,number2]}
and be able to access it like (pseudocode:) hash.get(3).get(1)
public class WebSearch {
readFile.ReadFile xfile = new readFile.ReadFile("inputgraph.txt");
HashMap webSearchHash = new HashMap();
ArrayList belongsTo = new ArrayList();
ArrayList keyphrase = new ArrayList();
public WebSearch() {
}
public void createGraph()
{
HashMap <Object, ArrayList<Integer> > outlinks = new HashMap <Object, ArrayList<Integer>>();
for (int i = 0; i < xfile.getNumberOfWebpages(); i++ )
{
keyphrase.add(i,xfile.getKeyPhrases(i));
outlinks.put(keyphrase.get(i), xfile.getOutLinks(i));
}
}
keyphrases is an ArrayList
this is my output of System.out.print(outlinks);
{[education, news, internet]=[0, 3], [power, news]=[1, 4], [computer, internet, device, ipod]=[2], [university, education]=[5]}
How would I go about getting say just this: [education, news, internet]=[0, 3]
I have tried:
outlinks.get(xfile.getKeyPhrases(i))
xfile.getKeyPhrases(0) would for example return [education, news, internet]
You can get the key set (Map.keySet()) of the map first; outlinks.keySet()
Then you can use these keys on your map to get your entries (values of the keys)
You haven't posted enough of the surrounding code for your question to be entirely clear, but look at the Javadocs for Map. You will probably get what you want by iterating over outlinks.values().
I recommend to use a customized object and use it inside your collections.
You may create a POJO/Bean class and overwrite the toString method with the details that you want, for instance the a iterate over items inside a array.
When you use it to print or display the toString method will be call.
The following link show you some ideas:
http://www.javapractices.com/topic/TopicAction.do?Id=55
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
You can access the keys of any HashMap using Map.keySet() method.
Also note that java.util.HashMap is unordered. HashMap makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
You would like to relook at the structure of your HashMap, you are having ArrayList as your key.

Adding items to empty List at specific locations in java

Is there any way I can make the below code work without commenting the 3rd line.
List<Integer> list = new ArrayList<Integer>();
list.add(0,0);
//list.add(1,null);
list.add(2,2);
I want to add items to list at specific locations. But if I don't change the index to Nth position I am not being able to add at Nth as told in this answer.
I can't use a map because I don't want to miss a value when the keys are same. Also adding null values to a list for large lists will be an overhead. When there is a collision I want the item to take the next position(nearest to where it should have been).
Is there any List implementation that shifts index before it tries to add the item?
Use something like a MultiMap if your only concern is not "missing a value" if the keys are the same.
I'm not sure how doing a shift/insert helps if I understand your problem statement--if the "key" is the index, inserting will lose the same information.
You can use Vector and call setSize to prepopulate with null elements.
However, your comment about the overhead of the nulls speaks to an associative container as the right solution.
This still smells like you should be using a Map. Why not use a Map<Integer, List<Integer>>?
something like,
private Map<Integer, List<Integer>> myMap = new HashMap<Integer, List<Integer>>();
public void addItem(int key, int value) {
List<Integer> list = myMap.get(key);
if (list == null) {
list = new ArrayList<Integer>();
myMap.put(key, list);
}
list.add(value);
}
public List<Integer> getItems(int key) {
return myMap.get(key);
}
Well, There are a couple of ways I would think to do this, if you are not adding items too frequently, then it might be a good idea to simply do a check to see if there is an item at that location before adding it.
if(list.get(X) == null)
{
list.add(X,Y);
}
Otherwise if you are going to be doing this too often...then I would recommend creating your own custom List class, and extending ArrayList or whatever you are using, and simply override the add method, to deal with collisions.

Categories

Resources