Weird results when using an ArrayList<ArrayList<String>> in java - java

I'm getting weird problems in my android app and I think it may be linked to the behavior of the way the ArrayList works. Check the following code and please tell me if I'm correct or doing something wrong:
ArrayList<ArrayList<String>> arr = new ArrayList<ArrayList<String>>();
ArrayList<String> tmp = new ArrayList<String>();
tmp.add("test");
arr.add(tmp);
tmp.clear();
After the last line the contents of arr[0] is emptied. So does that mean that when adding one ArrayList to another it does it by reference?
So if I have the following method:
void addArray(ArrayList<String> arr) {
group.add(arr); // group is ArrayList<ArrayList<String>>;
};
I must change it to:
void addArray(ArrayList<String> arr) {
ArrayList<String> tmp = new ArrayList<String>();
tmp.addAll(arr);
group.add(tmp); // group is ArrayList<ArrayList<String>>;
};
to make sure that if I clear the incoming array somewhere else that nothing happens to the group array?

In Java there is no passing by value, every object is passed by reference. So in your case arr[0] and tmp are the same object and clearing it will result in arr[0] being cleared. Hope this helps.
EDIT
As a quick answer to the second part of your question: you don't need to use the tmp ArrayList inside the addArray method. The argument of the addArray method is a reference to the object, passed by value. So changing it won't have any effect outside of the addArray method. Hope it's clear.

Yes, objects are not cloned when they're added to a collection.

For objects Java always passes a copy of the reference value, so yes, the ArrayList you are clearing is the same as the one you added to the other ArrayList.

Related

Java - from Array to linked ArrayList why it doesn't affect all int values?

I am studing Java, i have simple Array linked to ArrayList, it is fixed size i can change values inside array or list without change length.
So i tried to change all elements of the Array to see changes into ArrayList (it doesn't work). I saw that if i change single value into Array my list would change too (it works).
If i change my List values into array wuold changed.
If i change List or Array length would throw exception.
String[] nameListLinkedToArrayFixedSize = {"Jhonny","Joe","Jhoseph"};
List<String> nameListLinkedToArray = Arrays.asList(nameListLinkedToArrayFixedSize);
nameListLinkedToArrayFixedSize[1] = "J.Joe"; // this change my list
nameListLinkedToArrayFixedSize = new String[]{"ead","sda","eps"}; //change my array but non change my list
System.out.println(nameListLinkedToArray) // is same as first array why?
nameListLinkedToArray.set(2, "J.Jhoseph"); //[Jhonny, J.Joe, J.Jhoseph]
I need to understand how works linked arrays, i suppose this is not go well without point new array to new linked list?
Why single operation on array change list?
What is pointer of linked list after i change all element of array?
Why my list continues update old values of array?
Where to find specific documentation?
You have to dig into the code for Arrays.asList(). When you do, you will see that it uses the array you pass to it as the backing store. If you make changes to items in that array going forward in your code, those changes will also be seen in elements of the list.
However, if you assign your original array to a new array, you have effectively de-linked your external reference to the backing store, hence changes to elements of the new array are not reflected in the list.
import java.util.Arrays;
import java.util.List;
public class ArraysAsListSideEffects {
public static void main(String[] args) {
codeWithSideEffects();
codeWithoutSideEffects();
}
private static void codeWithoutSideEffects() {
System.out.println("\n\nCode without side effects: ");
String[] originalArray = { "Jhonny", "Joe", "Jhoseph" };
List<String> listFromArray = List.of(originalArray);
System.out.println(listFromArray);
originalArray[1] = "J.Joe";
System.out.println("After updating original array: " + listFromArray);
}
protected static void codeWithSideEffects() {
System.out.println("Side effects of using Arrays.asList()");
String[] originalArray = { "Jhonny", "Joe", "Jhoseph" };
List<String> listFromArray = Arrays.asList(originalArray);
// Because Arrays.asList uses your original array as the
// backing store, changing an element in the original
// array changes the element in the list.
originalArray[1] = "J.Joe"; // this change my list
// change my array but non change my list
originalArray = new String[] { "ead", "sda", "eps" };
// Since you assigned original array to a new array,
// changes to it will not affect the backing store reference
// used by listFromArray.
System.out.println(listFromArray); // is same as first array why?
// Yes because Arrays.asList stores a reference to that
// list.
listFromArray.set(2, "J.Jhoseph"); // [Jhonny, J.Joe, J.Jhoseph]}
// This is correct because of how Arrays.asList
// creates an ArrayList with your original array
// as backing store.
System.out.println(listFromArray);
}
}
Output:
Side effects of using Arrays.asList()
[Jhonny, J.Joe, Jhoseph]
[Jhonny, J.Joe, J.Jhoseph]
Code without side effects:
[Jhonny, Joe, Jhoseph]
After updating original array: [Jhonny, Joe, Jhoseph]
I think you understand that the list array value is defined upper but when you change the string[] it doesn't affect le list so you have to change it again

Clone a list and modify the property of an element to list does not work in Java

I tried to make a clone of an arraylist so there will be two lists created. After that, I tried to modify the property of an element in one of the lists. When I compared the lists, it should have given me false for equal result but instead it is true. I assume this got to do with the pointer of the element or list. Is there any solution to fix that?
My code is like this:
List<UnifiedBucket> ubsCopy = new ArrayList<>(ubs);
ubsCopy.get(14).setRawPolicy(null);
UnifiedBucket ub1 = ubs.get(14);
UnifiedBucket ub2= ubsCopy.get(14);
System.out.println(ub1 == ub2);
System.out.println(ub1.getRawPolicy().equals(ub2.getRawPolicy()));
what you want to have is a deep copy but the constructor does shallow copy , look at public ArrayList(Collection c)
if you want to make a deep copy use Iterator on ArrayList like this :
List<UnifiedBucket> UnifiedBucketClone = new ArrayList<>();
Iterator<UnifiedBucket> iterator = ubs.iterator();
while(iterator.hasNext()){
UnifiedBucketClone .add((UnifiedBucket) iterator.next().clone());
}

Setting the Value for Arraylist of Arraylist

Hello I want to Fill Up my Arraylist of Arraylist named as QuestionIdList_Section
and for that i have make the temp Arraylist named QUESTION_ID_Of_SectionId_Temp that will be clear after adding into QuestionIdList_Section
My Code is as Below so that you can understand how i have codded :
public static ArrayList<String> QUESTION_ID_Of_SectionId_Temp = new ArrayList<String>();
public static ArrayList<ArrayList<String>> QuestionIdList_Section = new ArrayList<ArrayList<String>>();
QUESTION_ID_Of_SectionId_Temp.add("Hello");
QUESTION_ID_Of_SectionId_Temp.add("Hiii");
QuestionIdList_Section.add(0,QUESTION_ID_Of_SectionId_Temp);
Log.i(TAG, "******Before " + QuestionIdList_Section);
Log.i(TAG, "******Before "+ QUESTION_ID_Of_SectionId_Temp);
QUESTION_ID_Of_SectionId_Temp.clear();
Log.i(TAG, "******After " + QuestionIdList_Section);
Log.i(TAG, "******After " + QUESTION_ID_Of_SectionId_Temp);
After Executing the Code i am getting Different Result for both Variable.
as Below :
******Before [[Hello, Hiiii]]
******Before [Hello, Hiiii]
******After [[]]
******After []
can some one Please Help me to Understand where i am lacking here. i want to clear temp arraylist so that i can put the different Values for UESTION_ID_Of_SectionId_Temp so for second index of QuestionIdList_Section i will set the Value different.
Thanks in Advance.
QUESTION_ID_Of_SectionId_Temp is just a reference.
So if you clear it, the value in QuestionIdList_Section will also be clear.
What you should do is
QUESTION_ID_Of_SectionId_Temp = new ArrayList<String>();
instead of
QUESTION_ID_Of_SectionId_Temp.clear();
The ArrayList associated with QuestionIdList_Section holds a reference of the ArrayList associated with QUESTION_ID_Of_SectionId_Temp.
Thus, when clearing the temp ArrayList, it will also reflect on the ArrayList which is in QuestionIdList_Section.
You might want to create a new instance of temp array and add it to the main array list as follows:
QUESTION_ID_Of_SectionId_Temp = new ArrayList<String>();
QuestionIdList_Section.add(QUESTION_ID_Of_SectionId_Temp);
After doing so, every element you add to QUESTION_ID_Of_SectionId_Temp will be shown in thew second index of QuestionIdList_Section.
You are clearing the QUESTION_ID_Of_SectionId_Temp array which means it has no elements. The second "After" print displays it empty.
The first "After" displays the contents of the QuestionIdList_Section which still contains the ArrayList above, which is empty now.

Arraylist empty when return

I am working on an android project and I am facing a problem and the problem is:
Arraylist empty when I return it.
Here is my java code:
ArrayList<ArrayList<Object>> container = new ArrayList<ArrayList<Object>>();
ArrayList<Object> itemRow = new ArrayList<Object>();
JSONObject jsonObj = new JSONObject(result);
JSONArray allElements = jsonObj.getJSONArray("Table");
Log.i("allElements", "" + allElements);
for (int i = 0; i < allElements.length(); i++) {
itemRow.add(allElements.getJSONObject(i).getString("ParentName").toString());
itemRow.add(allElements.getJSONObject(i).getString("ParentEmailID").toString());
itemRow.add(allElements.getJSONObject(i).getString("ParentContact").toString());
itemRow.add(allElements.getJSONObject(i).getString("ParentAddress").toString());
itemRow.add(allElements.getJSONObject(i).getString("ParentProfilePictureName").toString());
itemRow.add(allElements.getJSONObject(i).getString("StudentName").toString());
Log.i("itemRow", "itemRow at index: " + i + ", " + itemRow);
container.add(((i*2)/2), itemRow);
itemRow.clear();
}
return container;
In this code I have two Arraylist one for contain all the elements and another one for storing single row of elements. These Arraylist are loaded from JSONArray, all is working fine and I can print data from item row (Arraylist which take single row) and store into main Arraylist (container).
But when I return this Arraylist (container) and print in logcat it shows empty Arraylist like
[[], [], [], [], []].
I cannot understand why this happen please help me to solve this issue.
Thanks.
Because you did, It still refers to the object that is added to container
itemRow.clear();
You might like to reinitialize it
itemRow = new ArrayList<Object>();
Stop clearing the list, and it won't be empty anymore:
itemRow.clear();
You should create a new list at each iteration. Put the following line of code inside the for loop:
ArrayList<Object> itemRow = new ArrayList<Object>();
Remember that Java passes references to objects. So the container list holds a reference to the list you add to it. It doesn't make a copy of the list. So your current code adds several references to the same list object to the container list, and you clear the list each time you add it. It thus contains N references to the same empty list at the end of the loop.
Your assessment is misleading/incorrect, the ArrayList is not empty, and actually contains five elements.
Each element of the array list is an empty list. This is because of the last two lines within your loop:
container.add(((i*2)/2), itemRow);
itemRow.clear();
The first line adds the itemRow to the container, as you expect. The next line calls clear() on the row you've just added - so everything in the container will always be empty by the time your method exits.
It looks like this issue was caused by you trying to reuse the same itemRow object throughout the method, which isn't going to work. To fix your problem, move the
ArrayList<Object> itemRow = new ArrayList<Object>();
constructor inside the loop (as the first line), and then stop calling clear() on it at the end. Now each JSON element will have a separate row list created for it, and once you've added these to the container they will maintain their contents.
Your assumption that container actually copies every arraylist in itself is not right. it refers to those already created rather having copies of each List.
try this
container.add(((i*2)/2), itemRow.clone());
as it about JAVA referencing...

For-Each and Pointers in Java [duplicate]

This question already has answers here:
Why does the foreach statement not change the element value?
(6 answers)
Closed 5 years ago.
Ok, so I'm tyring to iterate through an ArrayList and remove a specefic element. However, I am having some trouble using the For-Each like structure. When I run the following code:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(String t : arr)
{
t = " some other value "; //hoping this would change the actual array
}
for(String t : arr)
{
System.out.println(t); //however, I still get the same array here
}
My question in, how can I make 't' a pointer to 'arr' so that I am able to change the values in a for-each loop? I know I could loop through the ArrayList using a different structure, but this one looks so clean and readable, it would just be nice to be able to make 't' a pointer.
All comments are appreciated! Even if you say I should just suck it up and use a different construct.
I think the best approach may be to use a for loop.
ArrayList<String> arr = new ArrayList<String>();
for (int i = 0; i < arr.size(); i++) {
String t = arr.get(i);
if (// your condition is met) {
arr.set(i, "your new value");
}
}
The problem is that you're trying to change the loop-scoped reference t to let it point to a new String instance. This ain't going to work. It does not refer the actual entry in the arraylist. You need to change the actual value of the reference. If String was mutable and provided a fictive set() method for that, you could in theory do
for (String t : arr) {
t.set("some other value");
}
or so, but that's not possible as it is immutable. Better get a handle of the entrypoint in the array itself using the normal for loop:
for (int i = 0; i < arr.size(); i++) {
arr.set(i, "some other value");
}
If you insist in using the enhanced for loop, then you need to replace String by StringBuilder, which is mutable:
for (StringBuilder t : arr) {
t.delete(0, t.length()).append("some other value");
}
Remember, Java is pass-by-value, not pass-by-reference.
For-each doesn't give you an index pointer, so you just can't use it to change an immutable value.
Either use a for-loop with an index or use a mutable type (like StringBuffer, not String)
An array of objects (like strings) in Java is a contiguous block containing an ordered series of references. So, when you have an array of 4 strings, what you really have is 4 references stored IN the array, and 4 string objects that are outside of the array but are referenced by its 4 elements.
What the for-each construct in Java does is create a local variable and, for each iteration, copy into that local variable the reference from the array cell that corresponds to that iteration. When you set the loop variable (t = " some other value") you are putting a reference to a new string, "some other value", into the local variable t, not into the array.
The contrasts with some other languages (like Perl) where the loop variable acts like an alias to the array/list element itself.
Your code is re-written by the compiler as something like this:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for (final Iterator <String> i = arr.iterator(); i.hasNext();) {
String t;
t = i.next();
t = " some other value "; // just changes where t is pointing
}
To do what you want you would have to write the for loop like this:
for (final ListIterator<String> i = arr.iterator(); i.hasNext();) {
final String t;
t = i.next();
i.set("some other value");
}
Iterator does not have the set method, only ListIterator does.
Basically you want to remove the String t from the list arr. Just do a arr.remove(t) and you could be done. But you can't do it while iterating over the same list. You'll get an Exception if you try to modify the list this way.
You have two options:
clone your list, iterate through the clone and remove the 'specific' String from the original list
create a list for delete candidates, add all 'specific' Strings to that list and, after iterating through the original list, iterate through the wastebin and remove everything you've collected here from the original list.
Option 1 is the easist, the clone can be made like:
List<String> clone = new ArrayList<String>(arr);
You seem to misunderstand how objects/references work in Java, which is pretty fundamental to using the language effectively. However, this code here should do what you want (apologies for the lack of explanation):
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(int i = 0; i < arr.size(); i++)
{
arr.set(i, " some other value "); // change the contents of the array
}
for(String t : arr)
{
System.out.println(t);
}
I believe, this is not related to immutable or mutable.
t = " some other value "; //hoping this would change the actual array
t does not hold the reference to actual object. Java copies the value from arraylist and puts that value into t so array list value does not get affect.
HTH
This has been answered well. Still here is my suggestion. The var t inside loop is only visible there. It will not be seen outside the loop. You could do t.set() if it was not String.
Use a StringBuffer rather than plain strings. This way the string within is mutable.
Strings are immutable. If you had a mutable type like StringBuilder/Buffer, you could change the string in your iteration. You do have references, remember.

Categories

Resources