I run into a problem. The result is updated while I try to change the temp in the following code( I did nothing on the result):
List<List<Integer>> result = new ArrayList<>();
result.add(new ArrayList<>());
List<List<Integer>> temp = new ArrayList<>();
for (List<Integer> list: result) {
list.add(10000);
temp.add(new ArrayList(list));
}
I do not know why the result turns out to be [[10000]] as well as the temp. Is there anything wrong with the add method like temp.add(new ArrayList(list))?
The code, as written, only loops once. Let's take a look:
List<List<Integer>> result = new ArrayList<>();
result.add(new ArrayList<>());
This initializes result, which is a List, whose elements are List<Integer>. You then add one element to result, so result is a single-element list. That one element is itself a zero-element list.
List<List<Integer>> temp = new ArrayList<>();
Here, you initialize temp to also be a List containing List<Integer> as its element type, but it's empty.
for (List<Integer> list: result) {
list.add(10000);
temp.add(new ArrayList(list));
}
Note that here, result only has one element. That one element is the thing you gave it on line 2, with result.add(new ArrayList<>());. This loop will iterate for each element of result, which will cause it to only loop once, because result only has one element.
When this happens, the first element of result will be updated to be a list containing 10000 rather than an empty list. So result will still be a singleton list, but its one element will now also be a singleton list containing 10000.
You then call temp.add(new ArrayList(list)). Calling new ArrayList(list) creates an ArrayList that contains all the elements that list does. When you call it, list is a singleton containing 10000, so the new ArrayList(list) is also a singleton containing just the number 10000. Then, you add that ArrayList to temp, which was previously an empty list. Now, temp is a singleton, and its one element is a singleton list containing 10000.
At this point, result and temp are both lists containing exactly one element, each of which is a list containing exactly one element, which is the number 10000. That's why result and temp have the value [[10000]], where [10000] is a list containing just the number 10000, and [[10000]] is a list containing just [10000].
Related
The question is came up when I was looking at one interview question:
Given a set of distinct integers, nums, return all possible subsets (the power set). And one solution is:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums==null || nums.length==0) return res;
dfs(res, new ArrayList<>(), 0, nums);
return res;
}
private void dfs(List<List<Integer>> res, List<Integer> list, int pos, int[] nums) {
res.add(new ArrayList<Integer>(list));
if(pos==nums.length+1) return;
for(int i=pos; i<nums.length; i++) {
list.add(nums[i]);
dfs(res, list, i+1, nums);
list.remove(list.size()-1);
}
}
}
Here in the helper function dfs, we have to add a copy of the list each time. Otherwise, the empty list will be added to the res multiple times(if doing res.add(list) instead of what in the solution). Why "new" is necessary?
Your code is a fairly standard recursive depth-first search (DFS) for all possible subsets of the set of numbers in the nums array.
When I run your code as it stands, it works nicely. For example I call it like this:
Solution app = new Solution();
int[] nums = { 5, 2 };
List<List<Integer>> subsets = app.subsets(nums);
subsets.forEach(System.out::println);
Output:
[]
[5]
[5, 2]
[2]
And you are quite correct: if in the helper function dfs we don’t make a copy of the list with new, the result will appear as a list of empty lists. I tried changing the first line of dfs to just:
res.add(list);
Now the output from the same call as above is:
[]
[]
[]
[]
And I’ll tell you what: this is not just 4 empty lists. This is the same empty list being printed 4 times.
Follow this link to see the difference live.
Why is this? After all, as you said, the helper method does add elements to the list within its loop:
list.add(nums[i]);
Without new the same list is passed to each recursive invocation of dfs. While elements are added to this list, they are also removed from it again:
list.remove(list.size()-1);
So for most of the time during running your code, the result list of lists, res will contain a list with elements in it (the same list multiple times), at the end all the elements will be removed from the contained list.
What new does, as you know, is taking a copy of the list. In this way the state of the list, which elements it contains, is preserved: no elements will be removed from the copy when elements are removed from the original list. So your result ends up holding all different lists and all with the elements in them that were there when the list was added to the result.
First, we should know the "list" used in the for loop contains an address which points to a List type.
So, when use res.add(list), we store the address into the res, instead of the value. Then, every time we run list.add(nums[i]) , we can find the change through address stored in the list or the address stored in res (actually they store the same address).
With the backtrack goes deeper, we add the same address(stored in variable list) repeatedly into the res, that's why you can find copy of the list, since they are all the same address.
So the "new" is necessary to make the res store different addresses which point to different temporary list.
No, you don't need new.
I've just checked:
import java.util.*;
public class Main
{
public static void addToList(List<List<Integer>> res, List<Integer> toAdd){
res.add(toAdd);
}
public static void main(String[] args)
{
List<Integer> a = new ArrayList<>();
a.add(5); a.add(2); // list a size is expected to be 2
List<Integer> b = new ArrayList<>();
b.add(3); // list b size is expected to be 1
List<List<Integer>> containerOfLists = new ArrayList<>();
addToList(containerOfLists, a);
addToList(containerOfLists, b);
// Should be 2
System.out.println("The list size is "+ containerOfLists.size());
// Should be 2
System.out.println("The nested list 0 size is "+ containerOfLists.get(0).size());
// Should be 1
System.out.println("The nested list 1 size is "+ containerOfLists.get(1).size());
// Should be 5
System.out.println("The nested list 0 element 0 is "+containerOfLists.get(0).get(0));
}
}
The result is as expected, no new was used in the helper and the container was not empty:
The list size is 2
The nested list 0 size is 2
The nested list 1 size is 1
The nested list 0 element 0 is 5
I don't know at what point exactly you get an empty list, but, I wonder if the problem in your case is about you creating an empty list as follows
List<List<Integer>> res = new ArrayList<>();
in your subsets method. There you may return an empty list with the res object, and you might mistakenly think the empty list is returned by dfs. I can only suppose.
What the difference between assigning an arraylist to another and using method addAll between two arraylists?
1 > arrayList = arrayList; //should assign value of later arrayList to the first.
2> arrayList.addAll(arrayList) //add all the data of the later list to first.
the first completely replaces the data in the list ?
the second one for appending data in the list(if it already has any) ???
if i do arrayList.add(arrayList) without assigning any data to the first list, will it insert any data ?
I did the following code for testing and found results that i do'not really know.
secondList.add("1");
secondList.add("2");
firstList = secondList;
Log.i("CHECK","first list = "+ firstList);
firstList.addAll(secondList);
Log.i("CHECK","Firs list add : "+firstList);
firstList.clear();
firstList.addAll(secondList);
Log.i("CHECK","Firs list add 2 : "+firstList);
Result were :
CHECK: first list = [1, 2]
CHECK: Firs list add : [1, 2, 1, 2]
CHECK: Firs list add 2 : []
i was expecting the last log to have result like : [1,2]
as mentioned in docs.oracle.com
addAll- Appends all of the elements in the specified collection to the
end of this list, in the order that they are returned by the specified
collection's Iterator.
and if there's no data in the list ? then what will addAll DO ?
When you do:
firstList = secondList;
What you are saying is actually "to make firstList and secondList refer to the same list". After the line is executed, there will only be one list and two variables both refer to that list.
This is why after you cleared firstList, secondList lost all the elements as well. They refer to the same thing. This has nothing to do with addAll. When you called firstList.addAll(secondList), you are basically adding appending an empty list to another empty list, which results in an empty list.
when you use arrayList = arrayList2; then you are assigning the reference of arrayList2 in first list. That means they are referring to the same list.
and when you use arrayList.addAll(arrayList2) then they are two different list reference.
Now come back to your code (lets denote firstlist as f, second as s)
secondList.add("1"); // f={}, s = {1}
secondList.add("2"); // f={}, s = {1,2}
firstList = secondList; // f= s = {1, 2}
Log.i("CHECK","first list = "+ firstList); // so printing 1,2
firstList.addAll(secondList);// it is actually adding itself.. so f= s = {1,2,1,2}
Log.i("CHECK","Firs list add : "+firstList);
firstList.clear(); // clear boths as s = f
firstList.addAll(secondList); // as boths are blank so overall blank
Log.i("CHECK","Firs list add 2 : "+firstList);
I learned about this in class, Java doesnt really specify when it passes by value or passes by reference, but for the sake of arrayList's, they are pass by reference unless you specifically create new elements. When you say
firstArray = secondArray;
firstArray gets the memory address of the second array, therefore when you cleared the first array, you actually cleared the memory which the second array also shares.
Good luck!
I want to add COPIES of data to my List but when I use .add, it adds a reference and not a copy. I'll try to explain what I mean.
List<List<String>> formattedTempMatches = new ArrayList<>();
ArrayList<String> rowFormattedMatches = new ArrayList<>();
rowFormattedMatches.add(matchesArray[0]);
rowFormattedMatches.add(matchesArray[1]);
rowFormattedMatches.add(matchesArray[2]);
formattedTempMatches.add(rowFormattedMatches);
//rowFormattedMatches.clear();
rowFormattedMatches.add(matchesArray[3]);
rowFormattedMatches.add(matchesArray[4]);
rowFormattedMatches.add(matchesArray[5]);
formattedTempMatches.add(rowFormattedMatches);
I've written my code outside of a loop to try to explain myself better. I want to add 3 elements to an ArrayList (of which the elements come from a normal array) then add that ArrayList to a list of lists. When the ArrayList is added to the list, I want to clear it and refill it with 3 more elements and then add it to the next index of the List. The problem is once I clear it, the data is removed from the list. If I don't clear it, the list has 6 elements at each index when there's only supposed to be 3. What should I do?
Apologies for my possibly confusing explanation.
The call of clear() empties the list. As you are using the same instance for each iteration, this will not work. What you can do instead of clearing the list is create a new instance:
List<List<String>> formattedTempMatches = new ArrayList<>();
ArrayList<String> rowFormattedMatches = new ArrayList<>();
rowFormattedMatches.add(matchesArray[0]);
rowFormattedMatches.add(matchesArray[1]);
rowFormattedMatches.add(matchesArray[2]);
formattedTempMatches.add(rowFormattedMatches);
rowFormattedMatches = new ArrayList<>(); // new instance of an empty list
rowFormattedMatches.add(matchesArray[3]);
rowFormattedMatches.add(matchesArray[4]);
rowFormattedMatches.add(matchesArray[5]);
formattedTempMatches.add(rowFormattedMatches);
List<Integer> rest = new ArrayList<Integer>();
rest.add(1);
rest.add(2);
rest.add(3);
rest.subList(0, 1);
rest.subList(1, 3);
println(rest);
int i = 1;
List<Integer> newRest = rest.subList(0, i);
newRest.addAll(rest.subList(i + 1, rest.size()));
println(rest);
println is method to print list, why the first println will print [1,2,3], but second println will print [1,3,2,3]
You can see the explanation in the javadoc for subList():
...The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa...
In other words, adding an element to a sublist adds it to the original list too.
As you can read in the official Java documentation:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
In your code, newRest holds a reference to rest, so when you add items to newRest you're also adding them to the original list. The proper way of doing this is initializing newRest with a new ArrayList() like this:
List<Integer> newRest = new ArrayList<Integer>();
newRest.addAll(rest.subList(0, i));
newRest.addAll(rest.subList(i + 1, rest.size()));
Hope this helps you.
I am trying to get a sublist of a List but I want the sublist to be serialized. I found out that when we get sublist from an ArrayList the sublist is not serialized.
To overcome this, this is what I am doing:
ArrayList serializedSublist = new ArrayList();
//getQuestions() returns RandomAccessSubList
getQuestions().addAll(serializedSublist);
//problem is in the line below. serializedSublist is empty.
getRequest().getSession().setAttribute("questionsForUser", serializedSublist);
Problem is that serializedSubList is empty in line 5, eventhough in line 3 getQuestions() returns a list back.
You're adding it backwards, no? Shouldn't it be
serializedSublist.addAll(getQuestions());
or, better, yet
ArrayList serializedSublist = new ArrayList(getQuestions());