ArrayList<ArrayList<Integer>> treeList = new ArrayList<>();
ArrayList<Integer> aList = new ArrayList<>();
ArrayList<Integer> bList = new ArrayList<>();
aList.add(1);
treeList.add(aList);
bList = treeList.remove(0);
aList.clear(); //bList will be cleared
I know that bList and aList will refer to the same object,so when aList.clear(), bList will clear too, is there any way to make bList a new object.
... is there any way to make bList a new object.
You can copy it; e.g.
bList = new ArrayList<>(treeList.remove(0));
See How to copy Java Collections list
Actually, this looks like a case that would benefit from writing your own custom classes.
As written, your treeList is an open data structure. Anything that has access to treeList or any of its component ArrayList objects can interfere with it. That's OK in some circumstances. But if you want to protect against having different parts of your codebase "mess up" the data structure then you should put the data structure behind an abstraction boundary; e.g.
public class MyThing {
private ArrayList<ArrayList<Integer>> treeList = new ArrayList<>();
public void add(ArrayList<Integer> l){
treeList.add(new ArrayList<>(l));
}
public void remove(int index) {
return new ArrayList<>(treeList.remove(index));
}
}
Notice that MyThing carefully copies the lists when it adds them and when it removes them so that one client of the MyThing API cannot interfere with another one via shared lists.
Obviously, there is a cost in doing this.
Try this. Pass the return of the remove as an argument to the ArrayList constructor.
ArrayList<ArrayList<Integer>> treeList = new ArrayList<>();
ArrayList<Integer> aList = new ArrayList<>();
ArrayList<Integer> bList = new ArrayList<>();
aList.add(1);
treeList.add(aList);
bList = new ArrayList<>(treeList.remove(0));
aList.clear(); //bList will be cleared
System.out.println(bList);
Prints
[1]
Alternatively, instead of assigning:
bList = treeList.remove(0);
you can use List#addAll
bList.addAll(treeList.remove(0));
Related
I want to create one sorted list out of my original list - without the Collections.sort(list) call changing the original list. So that I have one list unsorted and one being sorted - out of the same list.
Take a look at this code:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList();
list.add(5);
list.add(8);
list.add(3);
list.add(6);
System.out.println("Before method list is");
System.out.println(list);
ArrayList<Integer> theReturnedList = sorted(list);
System.out.println("After it is");
System.out.println(list);
}
private static ArrayList<Integer> sorted(ArrayList<Integer> list){
ArrayList<Integer> returnList = list;
Collections.sort(returnList);
return returnList;
}
The list object gets sorted - even though I am not calling the Collection.sort() method onto it. How can I avoid it?
Beacuse I thought this would happen...
public static void main(String[] args) {
String original = "I am an object created in main";
String theChangedObject = change(original);
System.out.println(original);
}
private static String change(String string){
String changed = string;
changed = "I was changed";
return changed;
}
The object orginal stays the same.
Your problem is a misunderstanding of how references work. Let's take a look at the method:
private static ArrayList<Integer> sorted(ArrayList<Integer> list){
ArrayList<Integer> returnList = list;
Collections.sort(returnList);
return returnList;
}
The line ArrayList<Integer> returnList = list; does not copy the list. It copies the reference to the list. What this means is that returnList and list will both refer to the same list. Changes in one will affect the other, because they are actually just different names for the same thing.
What you want to do is to make a brand new list containing the same values, which can be done with
ArrayList<Integer> returnList = new ArrayList<>();
returnList.addAll(list);
There is also a convenient ArrayList constructor that does this in one step:
ArrayList<Integer> returnList = new ArrayList<>(list);
Changes to returnList will not affect list because they are now two completely independent lists that just happen to contain the same values.
In your sorted method you are still calling Collection.sort on the original list. To avoid this you could create a shallow copy and return that e.g.
private static ArrayList<Integer> sorted(ArrayList<Integer> list){
ArrayList<Integer> returnList = new ArrayList<>(list);
Collections.sort(returnList);
return returnList;
}
On this line:
ArrayList<Integer> returnList = list;
you are just creating another reference to the same list (Object) in your sorted method and any change that you apply to it using this new reference will be reflected in you original reference because they point to the same object. You can do this to create a new list:
private static ArrayList<Integer> sorted(ArrayList<Integer> list){
ArrayList<Integer> returnList = new ArrayList<>(list); // the new keyword creates a new object on the memory heap
Collections.sort(returnList);
return returnList;
}
This time we are creating another ArrayList Object with the elements of you original list. This way the original list won't change when you sort the newer.
This behaviour doesn't apply on Immutable Objects like String or LocalDateTime. These cannot change their state after being created and instead return a new copy with the changes applied.
you can use Stream api
List<Integer> list = Arrays.asList(5,8,3,6);
List<Integer> newList = list.stream().sorted().collect(Collectors.toList());
The stream api offers several methods to work with collections immutably. This is the recommended way if you are using Java 8 or later.
I have an ArrayList containing custom objects.
What is the best way to make a separate ArrayList that has the exact same content, but isn't using the same references? As in, if I edit the first object in list1, it doesn't touch the first object in list2, but otherwise they look the same through and through.
Is it considered correct / good practice to do the following, or is there a built-in way?
List<MyObject> firstList = getArrayListFromSQLiteDb(criteria);
List<MyObject> secondList = new ArrayList<>();
for (MyObject object : firstList) {
MyObject newObject = new MyObject();
newObject.setField1(object.getField1());
newObject.setField2(object.getField2());
newObject.setField3(object.getField3());
secondList.add(newObject);
}
A simple way of doing this would be to clone the original ArrayList, thus not sharing references and having the other list remain untouched when you alter the original one. As #911DidBush mentioned, this will only work if the lists contents are cloneable and implement the clone() method correctly.
List<MyObject> firstList = getArrayListFromSQLiteDb(criteria);
List<MyObject> secondList = new ArrayList<>();
for(MyObject obj : firstList) {
secondList.add(obj.clone());
}
I'm trying to initialize a List in Java but I want to know if there's a more elegant way of initializing multiple lists with the same types.
So far I've done the following:
List<Model> list1 = new List<>();
List<Model> list2 = new List<>();
List<Model> list3 = new List<>();
But I'm trying to initialize about 10 different lists of the same type and it seems very ugly.
I've also tried doing:
List<Model> list1, list2, list3 = new List<>();
But this doesn't work.
After searching for the answer, all I could find were tips on how to initialize an array with multiple variables in one line using the asList() method but that's not what I'm trying to do.
Is this even possible?
You can use a Map where the key represents the list name and the value represents a List
Map<String,List<Model>> lists = new HashMap<>();
You can then populate the list in a for loop :
for(int i=0;i<10;++i) {
lists.put("list"+(i+1),new ArrayList<Model>());
}
You can access the lists using :
lists.get("list1").add(new Model(...));
lists.get("list2").add(new Model(...));
Disclaimer : I have not tried compiling this code since I am not on a computer.
If you have 10 lists or whatever, it's time to think: probably you need an array or list of lists.
List<List<Model>> lists = new ArrayList<>();
for(int i=0; i<10; i++) lists.add(new ArrayList<>());
// later in code instead of list5.add(...)
lists.get(5).add(...)
List is an interface (abstract type) and cannot be instantiated. You will have to use ArrayList as shown below. You can try:
List<Model> list1 = new ArrayList<Model>(), list2 = new ArrayList<Model>();
This should work as well
List<Model> list1 = new ArrayList<Model>(), list2 = new ArrayList<Model>(), list3 = new ArrayList<Model>();
The closest possible thing that you can do is following
List<Model> a = new ArrayList<>(), b = new ArrayList<>(), c = new ArrayList<>(), d = new ArrayList<>();
But either of the approach you consider has same memory consumption impact.
Here's my answer:
#SuppressWarnings({"unchecked"})
List<Model>[] lists = new List[3];
for(List list : lists) {
list = new ArrayList<Model>();
}
List<Model> list1 = lists[0];
List<Model> list2 = lists[1];
List<Model> list3 = lists[2];
Let's say we have got a List<List<String>> and want to make it completely unmodifiable. Simply passing it to a Collection.unmodifiableList would not suffice because the inner Lists can still be modified.
I would go with the following approach:
List<List<String>> someList;
Make it unmodifiable:
List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
tempList.add(Collections.unmodifiableList(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);
Is this approach okay?
This approach should work, as long as you don't keep references to the original, modifiable, lists. Such references could modify the lists wrapped by the unmodifiable lists.
The below code should do :-
List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
tempList.add(new ArrayList<>(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);
Here goes the test for this :-
List<List<String>> someList = new ArrayList<>();
List<String> l1 = new ArrayList<String>();
List<String> l2 = new ArrayList<String>();
l1.add("STR1");
l1.add("STR2");
l1.add("STR3");
l2.add("STR4");
l2.add("STR5");
l2.add("STR6");
someList.add(l1);
someList.add(l2);
List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
tempList.add(new ArrayList<>(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);
l1.add("STR7"); // The inner list reference is modified this doesnot cause the safelist internal structure to get changed
for(List<String> safeInnerList : safeList) {
System.out.println(safeInnerList);
}
I feel it doesnot work:-
List<List<String>> someList = new ArrayList<>();
List<String> l1 = new ArrayList<String>();
List<String> l2 = new ArrayList<String>();
l1.add("STR1");
l1.add("STR2");
l1.add("STR3");
l2.add("STR4");
l2.add("STR5");
l2.add("STR6");
someList.add(l1);
someList.add(l2);
List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
tempList.add(Collections.unmodifiableList(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);
l1.add("STR7"); // The inner list reference is modified which causes the
safelist internal structure to get changed
for(List<String> safeInnerList : safeList) {
System.out.println(safeInnerList);
}
The way to achieve immutability is to create defensive copies.
Data in
Whenever a mutable object is passed to your method(s), you create a deep copy of it. This should be the first thing you do, even before you check validity, if you want maximum security.
Wrapping a list into a Collections.unmodifiableList() isn't going to work here because there's no guarantee that the underlying list won't be modified by a third party. In other words, you're not in control of the instance.
A good way of creating immutable copies of lists is by using Google Guava's ImmutableList.copyOf() method, but remember that you need a deep copy, so you need to create immutable copies of the lists within the main list.
Data out
whenever you return a value, you make another defensive copy, so that changes to the returned object don't reflect back. Here you can use unmodifiable wrappers (e.g. ImmutableList.of()) on your lists because you're holding the only reference to the original list.
If you do both (copy on the way in, copy/wrap on the way out), your code will be safe and correct. Any other solution and no such general guarantees can be given, your code may or many not be correct.
I need to use generics for my nestList. What syntax I can use so that both Integer and String lists can be added to nested lists as well as of any other types ?
// integer list
List<Integer> listInteger = new ArrayList<Integer>(Arrays.asList(1, 2));
// string list
List<String> listString = new ArrayList<String>(Arrays.asList("abc", "xyz"));
// nested lists.
List nestedList = new ArrayList();
nestedList.add(listInteger);
nestedList.add(listString);
nestedList.add("A");
Since you want to store lists AND non-collection objects ("A") you should store Objects in your collection, like:
import java.util.*;
public class Main {
public static void main(String[] args)
{
// integer list
List<Integer> listInteger = new ArrayList<Integer>(Arrays.asList(1, 2));
// string list
List<String> listString = new ArrayList<String>(Arrays.asList("abc", "xyz"));
// nested lists.
List<Object> nestedList = new ArrayList<Object>();
nestedList.add(listInteger);
nestedList.add(listString);
nestedList.add("A");
}
}
Just note that List<Object> is just to avoid the compiler from complaining that your collection doesn't have a type. Effectively, List<Object> and List are the same thing.
You could have suppressed the warning using this:
import java.util.*;
public class Main {
#SuppressWarnings({"rawtypes", "unchecked"})
public static void main(String[] args)
{
// integer list
List<Integer> listInteger = new ArrayList<Integer>(Arrays.asList(1, 2));
// string list
List<String> listString = new ArrayList<String>(Arrays.asList("abc", "xyz"));
// nested lists.
List nestedList = new ArrayList();
nestedList.add(listInteger);
nestedList.add(listString);
nestedList.add("A");
}
}
But ultimately, the solution in general is not good.
I don't have all your requirements, but a better idea would be to have an object to store all your collections and objects. You code would be cleaner and free from #SuppressWarnings, which are considered bad.
Something like:
MyObj myobj = new MyObj();
nestedList.setIntegers(listInteger);
nestedList.setStrings(listString);
nestedList.setSomeProperty("A");
Make the type as Object as you are adding different types of Objects(list,String) into the nestedList.
List<Object> nestedList = new ArrayList<Object>();
You can also use type as List only if you are adding list Objects in nestedList.
List<List> nestedList = new ArrayList<List>();
But this will error out if you try to add nestedList.add("A") , also It will also prompt a warning for using raw types.
Making it List<Object> = new ArrayList<>(); would allow you to add any type of Object regardless of type.
Just change this line
List<List> nestedList = new ArrayList<List>();
And it is, because you are storing list in lists, the type is List :)