Java Collections HashMap, value changes unexpectedly - java

public class ListMap {
HashMap<Integer, List> mp = new HashMap();
List myList = new ArrayList();
Integer x = 0;
Integer y = 5;
void test() {
for(Integer i = 0; i < 5; i++) {
x = y;
myList.add("check-1a" + i);
myList.add("check-1a" + i + 1);
y = null;
System.out.println(x); // output=5
mp.put(i, myList);
myList.clear();
}
}
1) But after clearing the List with myList.clear() the values that was inside the Map also gets cleared.
I mean to say that the map key remains there but it contains an "empty" List
2) However regarding the Objects x & y, after setting y to null how come x doesn't change?

When you add an object to a map (or any other collection), you are adding a reference to that object, not a copy. When you then make changes to the object, these changes will also affect the references in the map.
When you want to store a copy of your list, you need to create a new one. This can be done like this:
mp.put(i, new ArrayList(myList));
An alternative (and in my opinion even better) solution would be to reinitialize myList in the beginning of each loop iteration by setting it to a fresh list object:
myList = new ArrayList();
myList.add("check-1a"+i);
myList.add("check-1a"+i+1);
mp.put(i,myList);
Note that the list doesn't get destroyed when you reinitialize the variable myList. You can think of the object living on inside the map*.
*although a more technically accurate description would be "the object lives on in memory and isn't garbage-collected as long as there is still a reference to it stored in the map"

You're adding the same list to the Map multiple times and clearing it each time, so no surprise that its empty. Solution: Don't clear the list, create a new one within the for loop. This way, the Map will hold a unique List for each Integer.
public class ListMap {
HashMap<Integer,List<String>> mp=new HashMap<>();
// List<String> myList=new ArrayList<String>(); // **** get rid of
Integer x=0;
Integer y=5;
void test(){
for(Integer i=0;i<5;i++){
List<String> myList=new ArrayList<String>(); // ****** here
x=y;
myList.add("check-1a"+i);
myList.add("check-1a"+i+1);
y=null;
System.out.println(x);//output=5
mp.put(i,myList);
// myList.clear(); // **** get rid of
}
}
Also as per Tom, don't use raw types if possible, and so declare your lists as List<String> and ArrayList<String>.

Related

Java List Collections: Why does get(new Integer(i)) work and not when I declare an integer object first and then pass it through

I'm having trouble wording the question, so hopefully the example helps. I am trying to figure out why within the List collection in Java, where the method get() is overloaded by get(O Object) and get(int index), that one of these snippets work and the other doesn't.
edit: some context for the code, I'm trying to come up with an alternative way to check hits/miss on a LRU cache. ListB is a list containing data values, and listA is the cache of arbitrary length. I'm trying to figure out how come the first snippet works when coming across an int from listB that is contained within the cache (listA) which in turn removes, and then adds the element to bring it to the front of the list.
If I declare an Integer object and pass it through the get method it does not work. But if I declare a new Integer object within the get method, it does. For example:
This works This snippet will remove the first occurrence of Integer i it comes accorss
List<Integer> ListA= new ArrayList<Integer>();
for(int i: ListB){
if(ListA.contains(i)){
ListA.remove(new Integer(i));
}}
This does not work even though an Integer object is being passed through, this does not work
List<Integer> ListA= new ArrayList<Integer>();
for(int i = 0; i < ListB.size(); i++){
Integer obj = new Integer(ListB.get(i));
if(ListA.contains(obj)){ //or list.contains(i), neither work
ListA.remove(obj); //Does not work
}}
You shoudn't be creating a new Integer instance when retreive it from the list:
List<Integer> ListA= new ArrayList<Integer>();
for(int i = 0; i < ListB.size(); i++){
Integer obj = ListB.get(i); // Get the object from List B, do not create a new object from a reference in List B
if(ListA.contains(obj)) {
ListA.remove(obj);
}}

Check if an ArrayList contains the same element as another ArrayList

I have used some other answers to get a solution to my problem. But I am wondering if there is a way to improve this further?
// Copy the masterList ArrayList and then sort in ascending order and then make a third
// ArrayList and loop through to add the 8 lowest values to this list.
ArrayList<Integer> sortedList = new ArrayList<>(Calculator.masterList);
Collections.sort(sortedList);
ArrayList<Integer> lowEight = new ArrayList<>();
for (int i = 0; i < 8; i++) {
lowEight.add(sortedList.get(i));
}
// Set TextView as the value of index 0 in masterList ArrayList, check if lowEight
// ArrayList contains the element that is the same as masterList index 0 and if
// so highlight s1 textview green.
s1.setText("Score 1 is " + String.format("%d", Calculator.masterList.get(0)));
if (lowEight.contains(Calculator.masterList.get(0))) {
s1.setBackgroundColor(Color.GREEN);
}
This works to an extent by highlighting the values that are in both masterList and lowEight but for example if the number 7 is in lowEight and appears 9 times in masterList it will highlight all 9 occurences. Is there a way to move the exact object from masterList to sortedList and then to lowEight and then a method to check the object and not just the value?
Let me provide a more concise example of what you're asking. Let us take the following code:
ArrayList<Integer> list1 = new ArrayList<Integer>() {
{
add(5);
add(5);
}
};
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
The output is:
true
true
But you would expect it to be:
true
false
Because the first and second element are different objects. The problem here is that although they are different objects, they are equal objects. The way the Integer class is written in Java, any Integer is equal to another Integer if they represent the same value. When you run the contains() method, it sees that the list does indeed contain an object equal to the one you provided (in this case they both represent a value of 5), and so it returns true. So how do we solve this problem? How do we tell one Integer object from another? I would write your own "Integer" class. Something like "MyInteger". Here's a very simple implementation you could use:
public class MyInteger {
private final int i;
public MyInteger(int i) {
this.i = i;
}
public int toInt() {
return i;
}
}
And then when we use it in our ArrayList code:
ArrayList<MyInteger> list1 = new ArrayList<MyInteger>() {
{
add(new MyInteger(5));
add(new MyInteger(5));
}
};
ArrayList<MyInteger> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
We get our expected output:
true
false
This works because our new MyInteger class implicitly uses the default equals() method, which always returns false. In other words, no two MyInteger objects are ever equal. You can apply this same principle to your code.

Global variable during recursion

I have a global variable masterList, which is a HashMap.
private static HashMap<ArrayList<String>, Integer> masterList =
new HashMap<ArrayList<String>, Integer>();
I have a recursive function, generateAnagram that puts ArrayLists of anagrams in this HashMap with the number of words in the list as the value. However, the HashMap starts to mess up after the first call,and previous ArrayLists are overriden with the new one I'm trying to add, but the previous value remains. This results in two keys with the same value.
Here's a screenshot of the results - Click [here] http://tinypic.com/r/ka1gli/8
private static void generateAnagram(Set<String> subsets, ArrayList<String> currList, letterMap wordMap) {
if (wordMap.count() == 0) {
System.out.println("Adding: " + currList);
masterList.put(currList, currList.size());
System.out.println("Current Master: " + masterList.toString());
} else {
for (String word : subsets) {
if (word.length() <= wordMap.count() && wordMap.isConstructionPossible(word)) {
//System.out.println("Word: " + word + " " + wordMap.isConstructionPossible(word));
wordMap.remove(word);
currList.add(word);
generateAnagram(subsets, currList, wordMap);
currList.remove(word);
wordMap.addBack(word);
}
}
}
}
It's not a good idea to use an ArrayList as the key of a HashMap. Each time you change the content of the ArrayList (by adding or removing elements), its hashCode would change, so even if it's already in the HashMap, get() and containsKey() won't find it, and put() will add it again.
You only have one instance of the ArrayList, which you keep putting in the masterList map, so you would have only one entry in your map if you didn't change the contents of that list all the time.
You need to look at this from the point of view of the parameters. The ArrayList reference is passed as an argument to your recursion call each time, but it still points to the same ArrayList. When you then put it into the hashmap, you are storing multiple references to the same, single, original ArrayList.
Therefore use ArrayList.clone() before adding it to the master list. Better still, store an immutable collection to ensure your hash doesn't get messed up in the HashMap:
HashMap<List<String>, Integer> masterList =
new HashMap<List<String>, Integer>();
...
ArrayList<String> tmp = (ArrayList<String>)currList.clone();
List<String> imm = Collections.unmodifiableList(tmp);
masterList.put(imm, imm.size());
"previous ArrayLists are overriden with the new one I'm trying to add, but the previous value remains."
If you do not want the previous values, you might need to do something like this
BEFORE SCENARIO:
final ArrayList<Integer> arrayList = new ArrayList<Integer>();
final HashMap<ArrayList<Integer>, Integer> hashmap = new HashMap<ArrayList<Integer>, Integer>();
arrayList.add(1);
hashmap.put(arrayList, 1);
arrayList.add(2);
hashmap.put(arrayList, 1);
System.out.println(hashmap);
OUTPUT : {[1, 2]=1, [1, 2]=1}
AFTER SCENARIO :
ArrayList<Integer> arrayList = new ArrayList<Integer>();
final HashMap<ArrayList<Integer>, Integer> hashmap = new HashMap<ArrayList<Integer>, Integer>();
arrayList.add(1);
hashmap.put(arrayList, 1);
arrayList = new ArrayList<Integer>();
arrayList.add(2);
hashmap.put(arrayList, 1);
System.out.println(hashmap);
OUTPUT : {[1]=1, [2]=1}

How to make ArrayList that work as two dimentional array in java?

I want to make arrayList object in java that work as two dimentional array. My question is how can we access value from specific dimention from arrayList.
in two dimentional array, if i want to access value then it can be as m[i][j].
But in arraylist how can i do that ?
You mean something like a List in a List??
May be something like...
List<List<...>> twoDList = new ArrayList<>();
i want to make a List, in which each List key contains another List inside it
It should more like you want some kind of Map, which is basically a key/value pair.
Map<String, List<String>> mapValues = new HashMap<>(25);
List<String> listOfValues = ...;
//...
mapValues.put("A unique key for this list", listOfValues);
//...
List<String> thatListOfValues = mapValues.get("A unique key for this list");
List<List<Integer>> list = new ArrayList<List<Integer>>();
list.add(new ArrayList<Integer>());
list.add(new ArrayList<Integer>());
list.get(0).add(5);
list.get(1).add(6);
for(List<Integer> listiter : list)
{
for(Integer integer : listiter)
{
System.out.println("" + integer);
}
}
This way you can get the items like
list.get(1).get(0); //second dimension list -> integer
EDIT:
Although it is true that you can use a Map if you are trying to use numeric indices for example for each list, like so:
Map<Integer, List<YourObject>> map = new HashMap<Integer, List<YourObject>>();
map.put(0, new ArrayList<YourObject>());
map.put(5, new ArrayList<YourObject>());
map.get(0).add(new YourObject("Hello"));
map.get(5).add(new YourObject("World"));
for(Integer integer : map.keySet())
{
for(YourObject yourObject : map.get(integer))
{
yourObject.print(); //example method
System.out.println(" ");
}
}
Although even then the accessing of Lists would be the same as before,
map.get(0).get(1); //List -> value at index
Obviously you don't need to use Integers as the generic type parameter, that's just a placeholder type.
The solution like List<List<..>> is slow then you should use one dimention array like
// Two dimentions: m and n
List<String> arr = new ArrayList<String>(m*n);
for (int i=0; i< m; ++i) {
for (int j=0; j<n; ++j) {
String str=arr.get(i*n + j);
//You code here
}
}
Memory is an important consideration here.
It can be acceptable to model a 2D (or higher dimension) array using a 1D container. (This is how the VARIANT SAFEARRAY of Microsoft's COM works.) But, consider this carefully if the number of elements is large; especially if the container allocates a contiguous memory block. Using something like List<List<... will model a jagged-edged matrix and can fragment your memory.
With the 1D approach, you can use the get(index) method on the ArrayList appropriately transformed:
Given the (i)th row and (j)th column, transform using index = i * rows + j where rows is the number of rows in your matrix.
An arraylist is not an object to make a 2 dimentional arrays. However you can use it anyway :
You can use :
new ArrayList<ArrayList<Object>>; //or
new ArrayList<Object[]>;
But you should implement your own matrix class because you will probably have some check to do and a function get(int row, int column) would be cool
Also consider Table collection provided by Google Guava library. ArrayTable is an implementation based on 2D array.
You cane define like this
1>
List<Object[]> list = new ArrayList<Object[]>();
Fetching
list.get(i)[j];
2>
List<Map<Integer,Object>> list = new ArrayList<Map<Integer,Object>>();
Fetching
list.get(i).get(j);

"Copy" nested array list

I have a nested ArrayList of the form
ArrayList<ArrayList<PointF>> nestedArraylist
I want to create a "copy" nestedArraylistCopy of nestedArraylist in the following sense:
The elements of nestedArraylistCopyshould be independent copies of the elements in nestedArraylist, i.e. should be ArrayLists holding the references to the same PointF objects in the original nestedArraylist.
Can I somehow use Collections.copy(dest, src) to do what I want? The documentation is not exactly detailed unfortunately...
Does the following code do what I want?
for(int i = 0; i < nestedArraylist.size(); i++)
nestedArraylistCopy.add(new ArrayList<PointF>(nestedArraylist.get(i)));
Is there a more efficient and or elegant solution?
Q1: after you Collections.copy, your new List object will contain the same elements as src (assuming the size is the same) which means, it holds same ArrayList<PointF> objects, hence the PointF objects are the same too. If you cannot get the info you want from the api java doc, read the source codes.
Q2: What you did is different from Collections.copy, since your copied arrayList has new Arraylist<PointF> as elements, but they contain the same elements (PointF) as the source list.
Q3: I don't know. because I don't know what do you want to have eventually. All new objects? Only ArrayList should be new objects, or all references?
According to my knowledge your solutions will update only references, as does Collection.copy(). You can use the below method, which I prefer:
List<ArrayList<PointF>> newList = new ArrayList<ArrayList<PointF>>(oldList);
A change of the old list would not affected to new list.
I tested your second option and it also has the property that changes to the old one will not affect the new List.
Note - These will also update only the references. If you change elements in your new list it will update old list too. I think Your second array will create brand new objects, but I am not 100% sure about that. I am adding my testing code below for you reference.
package test;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Lasitha Benaragama on 4/28/14.
*/
public class StckTest {
public static void main(String[] args) {
List<String> oldList = new ArrayList<String>();
oldList.add("AAAAA");
oldList.add("BBBBB");
oldList.add("CCCCC");
oldList.add("DDDDDD");
oldList.add("EEEEEE");
StckTest test = new StckTest();
List<String> newListCopy = new ArrayList<String>(oldList);
List<String> newListClone = new ArrayList<String>();
for(int i = 0; i < oldList.size(); i++)
newListClone.add(oldList.get(i));
test.printArray(newListCopy);
test.changeList(oldList);
test.printArray(oldList);
test.printArray(newListCopy);
test.printArray(newListClone);
}
public void changeList(List<String> oldList) {
oldList.remove(2);
oldList.add("FFFFF");
}
public void printArray(List<String> oldList){
for(int i = 0; i < oldList.size(); i++){
System.out.print(oldList.get(i)+",");
}
System.out.println();
}
}
You can use the clone() method to make a shallow copy of your object.
ArrayList<ArrayList<String>> nestedArraylist=new ArrayList<ArrayList<String>>();
ArrayList<String> x=new ArrayList<String>();
x.add("Deepak");
nestedArraylist.add(x);
System.out.println(nestedArraylist);
ArrayList<ArrayList<String>> nestedArraylistcopy=(ArrayList<ArrayList<String>>)nestedArraylist.clone();
System.out.println(nestedArraylistcopy);

Categories

Resources