hashmap values get changed by doing operations like retainAll() [duplicate] - java

This question already has answers here:
In Java, what is a shallow copy?
(11 answers)
Closed 4 years ago.
String [] keys0 =a.keySet(). toArray (new String[a.size()]);
Map< String , TreeSet <Integer>> Sub_hash=new HashMap< String, TreeSet <Integer>>();
for(ko = 0 ; ko < keys0 . length ; ko ++) {
Set<Integer> so = test.get( keys0[ko] );
System.out.println(""+so);
for(ko1 = ko+1 ; ko1 < keys0.length; ko1++) {
Set<Integer> so1 = test.get( keys0[ko1] );
boolean tr=so1.retainAll(so);
if(tr && so1.size()>=2) {
Sub_hash. put(keys0[ko]+keys0[ko1], (TreeSet<Integer>) so1);
System.out.println(""+Sub_hash.size()+""+Sub_hash);
}
}}
this is my second post and i dont know how to post neatly the requirement is i have a hash map with keys and values where i need to compare with one key in the map with another key in the map and retain the values and put the result in the sub_hash map but the problem is the original values of the map are changing as the values of the map are updated by the method retainAll();
but when the iteration comes to second key the values are changed completely but the comparison goes like this 2-3,2-4,2-5,2-6,2-7...etc but
as the values are changed result is completely erroneous so is there any chance to make it constant like some variable final to the hashmap.

Here's your problem:
Set<Integer> so1 = test.get( keys0[ko1] );
boolean tr=so1.retainAll(so);
You don't want to modify values in test. You want to modify a copy instead:
Set<Integer> so1 = new TreeSet<>(test.get( keys0[ko1] ));
boolean tr=so1.retainAll(so);

A Map (or any collection) can only contain references to objects, not the objects themselves. When you put a copy of a reference into a collection or get a reference from a collection, you are copying just the reference, not the underlying object.
When you get a set from a Map, and you modify the set, there is only one copy of that set.
NOTE: The Map is not altered when you alter a Set in it.
If you want a copy of a collection, you have to do this explicitly.

Related

Have problem with static varible in java, can't add element on ArrayList

I'm trying to add, delete and edit the Slang (for example the key is: 3, it has a value of cute).
I have to use Java Swing. I have to use Java extra Swing. Because working with many classes at the same time, the variable that stores the [key, value] I set the variable static.
private static Map<String, ArrayList<String>> multiMap = new HashMap<String, ArrayList<String>>();
I have the operation that is adding 1 data slang. If it matches, it will add value to the existing slang.
I noticed, if you add a slang, it means adding a pair of [key, value], then when you find that pair of [key, value], you will find it. When adding value to the existing slang, I could not find it.
I'm guessing that since multiMap is static and ArrayList is not static if we only change the value of ArrayList, when accessed elsewhere it will not be updated. So in case of entering the same key, I choose to delete the old key and add a new one.
String[] valueStrings = null;
if(value.length() > 0){
valueStrings = value.split("[|]");
for(int i = 0; i <valueStrings.length; i++){
valueStrings[i] = valueStrings[i].trim();
}
}
if(choose == 0){ // 0 it mean new
Slang.multiMap.put(NewSlangs, new ArrayList<>());
Slang.multiMap.get(NewSlangs).addAll(Arrays.asList(valueStrings));
}
else if(choose == 2){ // 2 it mean old
ArrayList<String> tempArrayList = new ArrayList<>();
tempArrayList.addAll(multiMap.get(NewSlangs));
tempArrayList.addAll(Arrays.asList(valueStrings));
Slang.multiMap.remove(NewSlangs);
Slang.multiMap.put(NewSlangs, tempArrayList);
}
After an error is added to the new key, value, when looking again, it will be found. But not with inserting value. So I debug and have some problems here.
When adding new Array List
The new ArrayList address I added is# 1893 and I found it using the search function
When adding value
, error.
The old ArrayList contains old values with the address # 2050
image
The new ArrayList contains new values whose address is # 2088
But when looking for it, it only contains the old 4 values
image
image
and its address is also different.
So I am asking why and how to fix it?
Your code seems to be unnecessarily complex. I suggest simplifying it and then coming up with a simple example that demonstrates the problem you are having. A simple implementation of a multi-map would look like:
for (String strValue: value.split("|")) {
multimap.computeIfAbsent(key, k -> new ArrayList<>()).add(strValue.trim());
}
There is no need to create temporary lists nor to remove a map entry before adding a new value for the same key.

Can we assign a variable for looping to a value inside the loop in Java?

I would like to assign a certain value to an iteration variable inside a for loop in Java.
List<Integer> values = Arrays.asList(1,2,4,16,32,64,128);
for (Integer value: values) {
value = value / 2; // local value
}
Is this assignment working in java?
No you can't and also its not recommended however in your case
List<Integer> values = Arrays.asList(1,2,4,16,32,64,128);
for (Integer value: values) {
value = value / 2; // local value even its modify it will not affect in collection so you will not get an error.
}
Java is having value type and reference type. so if you are modifying anything inside a foreach loop and its value type it will work and its local value so it will not modify a collection, but if its reference type you will get an error.
In for-each loop, we can’t modify collection, it will throw a ConcurrentModificationException
Modifying a collection simply means removing an element or changing content of an item stored in the collection.
If you want to modify a collection use iterator
if you just need traversing use foreach
for more please visit here
You can do it by hacking your way, but it is very suspicious and if you really need this, you are probably doing something wrong.
What you may need is to have another collection where you can put processed values,
List<Integer> values = Arrays.asList(1,2,4,16,32,64,128);
List<Integer> newValues = new LinkedList();
for (Integer value: values) {
newValues.add(value / 2);
}
Or if you ask if you can simply reassign iterator value, sure it is possible, but again is a bit suspicious. Maybe you can use it in such a case;
List<Integer> values = Arrays.asList(1,2,4,16,32,64,128);
for (Integer value: values) {
if(value > 32)
value = 32;
process(value);
}
Since i only reassign the iterator value it is safe, however editing the collection during looping over it will either result in a ConcurrentModificationException or worse(hacky way) as you cannot always determine the behavior of your list implementation.

HashMap dont stay stable in android

Hi I am trying to save a object in HashMap if it is not exist than if it is exsit I want to control its value with new data. If data change than I want to do sth. else. But whenever I tried to compare new data and hash value I saw they are same on every time . How can I handle with this issue. There is code:
BluetoothLeDevice deviceLe;
private Map<String, byte[]> mMacMap;
byte [] integer0 =new byte[4];
byte[] tempInteger0=new byte[4];
public void addSensor(String macId, BluetoothLeDevice deviceLe) {
byte [] addSensorrecord=deviceLe.getScanRecord();
int j=0;
for(int i=15;i<19;i++)
{
integer0 [j]=addSensorrecord[i];
j++;
}
if (mMacMap.containsKey(macId)) {
tempInteger0 = mMacMap.get(macId);
if(!integer0 .equals(tempInteger0))
{
mMacMap.remove(macId);
mMacMap.put(macId, integer0 );
new SendBLEData().execute(deviceLe);
}
} else {
final byte [] LocalInteger0=new byte[4];
int t=0;
for(int i=15;i<19;i++)
{
LocalInteger0[t]=addSensorrecord[i];
t++;
}
mMacMap.put(macId, LocalInteger0);
new SendBLEData().execute(deviceLe);
}
}
I am guessing, that your problem is here:
!Integer0.equals(tempInteger0))
I think you want to compare two arrays; and you are surprised to find them to be different ... all the time.
Your problem is: equals() on arrays doesn't do a comparison of the array content. In other words: this call to equals() only gives "true" if the arrays you are comparing ... are one and the same, like in:
int a[] = { 1 };
int b[] = a;
int c[] = { 1 };
Here:
a.equals(b) --> true
but
a.equals(c) --> false
When comparing array content matters, then you should use ArrayList instead. Because two ArrayList objects are equal when they contain exactly the same equal elements.
And you see: you are using that equals on arrays to make a decision in your code. So, you either change to ArrayLists; or use Arrays.equals() as user hamsty suggested.
Just a few additions to the already posted answers.
The remove below is not necessary, a simple put will replace the old value
mMacMap.remove(macId);
mMacMap.put(macId, integer0 );
From the javadoc
If the map previously contained a mapping for the key, the old value
is replaced by the specified value. (A map m is said to contain a
mapping for a key k if and only if m.containsKey(k) would return
true.)
Have you considered making bytes 15-19 into a string and adding them onto the maps key? This would eliminate the array compare and make the lookups much faster.
!Integer0.equals(tempInteger0))
is your problem.
Use this to compare the content of arrays:
Arrays.equals(Integer0, tempInteger0)
The problem is the following sequence of events:
macId not in mMacMap, insert new byte[4]; into the map
macId in mMacMap, the array created in the previous step never matches integer0 due to the Array comparison problem mentioned by the other answer, replace macId in the map with a reference to integer0
macId in mMacMap, since the array is a reference to integer0, it will always compare positively and the contents will no longer be updated.
Repeat 3.
Basically caused by these two issues:
Array#equals does not behave intuitively, use the static method Arrays.equals
Java is heavily reference-based, so if you insert something into a map it will not be copied, but simply a new reference is created; this may bite you if you change a shared object afterwards (like the array).

Bug in HashMap / ArrayList or wrong code? [duplicate]

This question already has answers here:
ArrayList as key in HashMap
(9 answers)
Closed 6 years ago.
Tired up with trying to resolve the problem with this code:
public class MapTest {
static class T{
static class K{}
}
static Map<List<T.K>, List<String>> map = new HashMap<>();
static List<String> test(List<T.K> list, String s){
List<String> l = map.get(list);
if (l == null){
l = new ArrayList<String>();
System.out.println("New value()");
map.put(list, l);
}
l.add(s);
return l;
}
public static void main(String s[]){
ArrayList<T.K> list = new ArrayList<T.K>();
test(list, "TEST");
list.add(new T.K());
List<String> l = test(list, "TEST1");
System.out.println(l.size());
}
}
It should create a new list-value for the map only once, but output is as follows:
New value
New value
1
it is something wrong happen with hashcode of the list after I insert value in it.
I expect "new value" show up only once, and size will be 2, not 1.
is it just JVM problem or something more general?
mine one is Oracle JVM 1.8.0_65
The hashcode of the list object changes when you put an item in it. You can see how the hashcode is calculated in the ArrayList.hashCode documentation.
In general, using a mutable object as the key for a map isn't going to work well. Per the Map documentation:
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 in a manner that affects equals comparisons while the object is a key in the map.
Thus, when you add the list to the map a second time, the map doesn't see it as being "equal" to the first list (since it isn't according to .equals), so it adds it again.
If you want a map where keys are looked up by identity rather than by value, you can use the IdentityHashMap class.

How to insert array into hashtable w/o initializing in Java?

I have a hashmap initialized as follows:
Hashmap<String[][], Boolean> tests = new Hashmap<String [][], Boolean>();
I would like to insert into tests without having to initialize the key:
tests.put({{"a"}, {"a"}}, true);
However, Java doesn't seem to let me do this. It works if I do it like this:
String[][] hi = {{"a"}, {"a"}};
tests.put(hi, true);
Is there any way to avoid the latter and get the former working?
Can someone also explain the reasoning behind this error?
Thanks
Yes, you can write like this:
tests.put(new String[][] {{"a"}, {"a"}}, true);
This is often referred to as an anonymous array or a just-in-time array.
In your case you would have to use
tests.put(new String[][]{{"a"}, {"a"}}, true);
because as you noticed {{"a"}, {"a"}}
String[][] hi = {{"a"}, {"a"}};
can be used only while creating reference to array.
You can use
tests.put(new String[][]{{"hello", "goodbye"},{"hi", "bye"}}, true);
This is almost definitely not what you want.
Arrays in Java get their equality and hash code from Object -- which is to say, based on their reference identity. So:
String[] a = { "hello" }; // create one array
String[] b = { "hello" }; // create a different array with the same contents
assert a != b; // the two references are to different objects
assert ! a.equals(b); // they're not equal
assert a.hashCode() != b.hashCode(); // neither are their hashes (probably)
a and b will not be equal, and their hash codes will almost certainly not be equal, since they are different objects. This means that if you use an array as the key to a hash map, you won't be able to retrieve the value using an key but the exact one that you created it with: any other array will have a different hash code and will be non-equal, and therefore won't be considered an equivalent key.
The solution is to replace the String[][] with a List<List<String>>. Lists define equality and hash codes based on their contents, so a list containing [ "hello" ] is equal to any other list containing [ "hello" ]:
List<String> x = Arrays.asList("hello");
List<String> y = Arrays.asList("hello");
assert x != y; // the two lists are different objects
assert x.equals(y); // but they're equal
assert x.hashCode() == y.hashCode(); // and so are their hash codes
Now you can use the lists as keys. Keep in mind that once a list is a key to the map, it's not allowed to change values. Doing so will probably break the hash map, because the list's hash code will have changed, but the map won't know about it, so the map will look for it in the wrong hash bucket.
The easiest options here are:
be sure that nobody else has a reference to that same List object and might change it
create a deep copy of the List before you put it into the map (that is, copy the "inner" lists as well as the "outer" one)
For the second option, it'd be something like:
// copy the outer list
List<List<String>> outerCopy = new ArrayList<List<String>>( originalList );
ListIterator<List<String>> listIterator = outerCopy.listIterator();
while (listIterator.hasNext()) {
// make a copy of the inner list
List<String> innerCopy = new ArrayList<String>( listIterator.next() );
listIterator.set(innerCopy);
}

Categories