Collections.sort sorts the wrong list - java

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.

Related

How to clone the List correctly in the constructor?

The constructor takes a List as parameters, this List needs to be filled with the same elements, for example "1" and cloned 7 times, assign the result to different collections, so that then 7 methods would process each of their lists.
constructor call:
public Class1 {
public Class2 arrayList = new Class2(new ArrayList<>(10000))
public Class2 linkedList = new Class2(new LinkedList<>())
public Class2 cowList = new Class2(new CopyOnWriteArrayList<>())
}
public Class2 {
private List<Integer> list;
private List<Integer> list2;
private List<Integer> list3;
private List<Integer> list4;
private List<Integer> list5;
private List<Integer> list6;
private List<Integer> list7;
public Class2(List<Integer> list) {
for (int i = 0; i < list.size(); i++) {
list.add(1);
}
this.list = list;
this.list2 = list;
this.list3 = list;
this.list4 = list;
this.list5 = list;
this.list6 = list;
this.list7 = list;
}
method1(){
// list - doSomething...;
}
method2(){
// list1 - doSomething...;
}
method3(){
// list2 - doSomething...;
}
method4(){
// list3 - doSomething...;
}
...
}
Each method performs a specific operation on its list. Depending on the constructor being called, the list is - ArrayList or LinkedList or CopyOnWriteArrayList
My code doesn't work. What am I doing wrong?
With the below statements, all your lists point to the same memory location and hence any operation on the list would be visible to all of list1 to list7.
this.list = list; this.list2 = list; this.list3 = list;
Instead you need to generate a new arraylist containing the elements of the input list using the appropriate list constructor which accepts a Collection as the argument.
Since the input could be of any List type, you could make use of instanceof to check the type of input and invoke the appropriate constructor.
NOTE: You are only creating a new list. So changes to the original list won't be reflected on those seven lists. BUT the objects contained in the list are still referenced in the new lists as we are not cloning the object. We are just creating a new list with the same objects. SO if you were using List<SomeObject> instead of List<Integer>, were SomeObject is mutable, then any operations on the objects would be reflected on every list. This is not applicable to this specific question as it uses immutable Integer objects.
private static List<Integer> copy(List<Integer> input) {
if(input instanceof ArrayList) {
return new ArrayList<>(input);
} else if (input instanceof LinkedList) {
return new LinkedList<>(input);
}
// others
...............
...............
}
Now invoke this method in your class' constructor.
this.list = copy(list);
this.list2 = copy(list);
this.list3 = copy(list);
You could also replace the for loop at the beginning with the below:
Collections.fill(list, 1);
But with the current code, all inputs are empty and hence the size of every list you are passing would be 0. Hence no filling of elements would take place (using for loop or Collections.fill)
new ArrayList<>(10000) does not update the size of the arraylist. It only initializes the backing array.
Read this too.

How can i get a new object in arrayList.remove()

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));

Array of references to ArrayList Object

I want to know the shortcut for doing this :
class{ ArrayList a1=new ArrayList();
ArrayList a2=new ArrayList();
ArrayList a3=new ArrayList();
.....& so on
}
So instead of writing each reference, how can I write an array of references 'with ArrayList' ?
you can use `List<ArrayList> list = new ArrayList<ArrayList>();`. In this you can add any number of `ArrayList` Objects to `list` reference.
Find the below :
List<ArrayList> list = new ArrayList<ArrayList>();
list.add(new ArrayList());
list.add(new ArrayList());
list.add(new ArrayList());
list.add(new ArrayList());
// ....
list hold ArrayList references in inserted order. You can access the ArrayList using index which zero based.
// to access the ArrayList reference.
ArrayList a1 = list.get(0);
Use an array of ArrayList. And loop.
ArrayList a1, a2, ... , a100;
ArrayList[] arrayLists = new ArrayList[100];
for (ArrayList arrayList : arrayLists) {
arrayList = new ArrayList();
}
But creating different references array is not a good design.
For example you can create array list array like below:
ArrayList[] a = new ArrayList[4];
Before implementing this you clear with your requirement , why you need to create these many references.
And also while creating ArrayList it's better to use reference creation like below:
List<String> a = new ArrayList<String>();
Always use generics while working on JAVA collections.
For your requirement to store word and it's permutations, I would suggest below process:
Step1: Create WordPermutations class
public Class WordPermutations{
public string word;
public ArrayList<String> permutations = new ArrayList<String>();
public void setWord(String word){
this.word = word;
}
public void setPermutations(ArrayList<string> permutations){
this.permutations = permutations;
}
public String getWord(){
return this.word;
}
public ArrayList<String> getPermuatations(){
return this.permuations;
}
}
Step2: Use this class to create your arrayList objects like below:
Class MainPermutation{
public static void main(String args[]){
// your code to get all your words and their permutations
// Now add them like below
ArrayList<WordPermutations> words = new ArrayList<WordPermutations>();
//your loop to iterate words and their permutations
for (int i=0;i<words.length;i++){
WordPermutations wordP = new WordPermutations();
wordP.setWord(word); //String word
wordP.setPermutations(permutations); //arraylist of permutations
words.add(wordP); //adding your word and it's permutations to class
}
}
}
Step3: Now read your arrayList array accordingly
with above process you can store n number of words.
Hope it helps you.

Collection.sort(arraylist) in a function sorting another arraylist

say, I made an arraylist in (public class class1(String args[]))
static List<Double> list1 = new ArrayList<Double>();
then I pass this arraylist to a function in (public static void main(String args[]))
biggestvalue(list1);
this is the function for example:
public static double biggestvalue(List<Double> list){
Collections.sort(list);
return list.get(list.size()-1);
}
I pass it into a function so that hopefully it will only sort list but not list1, but then list1 gets sorted as well, and I do not understand why that is.
Therefore, please explain to me why that is, and what solutions to this error are out there?
You only pass a reference to the List when you pass it as an argument. Therefore, both list and list1 point to the same List.
A good rule of thumb is to not modify objects passed into a method, so I would make a copy inside the method:
public static double biggestvalue(List<Double> list){
List<Double> temp = new ArrayList<>(list);
Collections.sort(temp);
return temp.get(temp.size()-1);
}
list1 gets sorted as well because you are passing a reference to that object. You want to duplicate the list before sorting it, so the original object doesn't get modified:
List<Double> dup = new ArrayList<>(list);
Collections.sort(dup);
return dup.get(dup.size() - 1);

ArrayList won't return modified after running through a method

I can't get this method to modify an ArrayList then return the modified ArrayList.
public static ArrayList<Integer> sortAndRemoveDuplicates(ArrayList<Integer> list) {
list = new ArrayList<Integer>(new HashSet<Integer>(list));
Collections.sort(list);
//System.out.println(list);
return list;
}
When I uncomment the System call I can see that the ArrayList is being modified and I know that my method works but when I run the method through main the ArrayList remains unaffected. Why is this? What am I doing wrong?
Why isn't this modified version working?
public static ArrayList<Integer> sortAndRemoveDuplicates(ArrayList<Integer> list) {
ArrayList<Integer> newList = new ArrayList<Integer>(new HashSet<Integer>(list));
Collections.sort(newList);
System.out.println(newList);
return newList;
}
You are creating new ArrayList from passed list so You need to catch result back
List<Integer> result = sortAndRemoveDuplicates(inputList)
Java passes references by value. You are re-assigning the input reference that points to the initial list to another object. The original list will remain pointing to the original ArrayList object.
You need to assign this newly created list back to the original reference:
originalList = sortAndRemoveDuplicates(originalList);
EDIT: Alternatively, since you are not returning the value to the caller method (main), you can make your method's return type void and modify your input list as follows:
public static void sortAndRemoveDuplicates(List<Integer> list) {
List<Integer> newList = new ArrayList<Integer>(new HashSet<Integer>(list));
Collections.sort(newList);
System.out.println(newList);
list.clear();
for(Integer integer : newList) {
list.add(integer);
}
}
Note: It is best practice to program to interfaces instead of concrete implementations, i.e. let your newList be declared of type List instead of ArrayList.
I think your expectations are not met because everything in Java is pass by value so the modifications to the list are made to a copy, not your original list. Unless you return and capture the modified copy, you will not see the changes in the calling code on return from the method.

Categories

Resources