today i dealt with a Java problem that really confused me.
I have the following code:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // getter returns 2 elements
someList.add(new ObjectXY());
obj.getListOfObjectsXY(); // getter now returns 3 elements
When i add an element to a list, the getter gets some kind of overwritten. Is this because someList acts like a reference on the result of the getter in this case? Or what else causes this effect?
I solved the problem with the following code by using another list:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // result: 2 elements
List<ObjectXY> someOtherList = new ArrayList<ObjectXY>();
someOtherList.addAll(someList);
someOtherList.add(new ObjectXY());
obj.getListOfObjectsXY(); // result: 2 elements
But i am still some kind of confused because i didn't expect Java to behave this way.
Can anyone explain to me what i did wrong and why it is so?
Thanks in advance!
The returned result is indeed just a copy of a reference to the same object as you are using internally. Counting on the caller to not modify the object is error-prone.
One solution is to return a reference to an unmodifiable list wrapping your list. See Collections.unmodifiableList(). The getter caller will be unable to modify your list.
Is this because someList acts like a reference on the result of the
getter in this case?
Yes. The list you received was just a reference to the same, original list you had. Any changes made on this variable would be reflected on the original list.
By adding the list's values to a new list you explicitly constructed a new object and thus they are separated.
In your case, obj.getListOfObjectsXY() everytime return you the same object and in Java object references are pass-by-value. So, when you do a someList.add(new ObjectXY());, it's actually setting the property of the object someList which is poiting to obj.getListOfObjectsXY().
And in the latter case, you are just copying someList to someOtherList. Then you added one more element to the same someOtherList but not to the someList. So, you getting 2 elements in
obj.getListOfObjectsXY(); is perfectly valid.
Related
Let's say I have an Object called Index, this object has two attributes Long id; , String name; and I have two ArrayLists in my page, the problem is that when I edit the name of the index object in the first list it is being edited in the second list as well, here is my code to make the problem more understandable:
Index index1 = new Index();
index1.setName("1");
index1.setId(1);
List<Index> indexes = new ArrayList<Index>();
indexes.add(index1);
List<Index> newIndexes = new ArrayList<Index>();
newIndexes.add(index1);
Now if I update the name of the index in the indexes list it is being updated in the newIndexes list. note: the object index has equals method on the Id field.
Thanks
The reference index1 is the same for both lists, so changing the Index referenced will change it in both.
Cloning the List per se won't fix your issue, as it'll clone the List but not its elements.
You need to perform a deep-clone of the List and its elements (or initialize a new ArrayList, as you do, and clone each of the previous List's elements) to solve your issue.
See here for the how-to.
That is because index1 is just a reference to the object. So, your are basically adding the same reference to both of the lists. You need to copy the object before adding to the second list.
When you add the object to both the lists, the reference of that object is copied to the lists. And that's why when you object the object from one list, it is reflected back in the other. To avoid this, you need to create a copy of the object and add it to the other list, so that both do not refer to the same object.
This is happening because when you use "Add" on array list (and in almost every data collection object) the collection is adding the "reference" of the object to its list, and not creating a new object.
Thus, when both index1 objects at indexes and newIndexes are basically the same one. When changing it no matter where, it will be changed at the other as well.
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 8 years ago.
Here a beginners question.
Is there any difference in JAVA between passing an object as argument to a method or returning that object from the method. For instance: Is it better to pass a List as an argument and fill it in the method or just allow the method to return a list?
My guess is that it should be no difference since a reference is returned and nothing is copied. But is there something more subtle behind?
thanks in advance
Altober
Many of the comments seem to have misunderstood what you mean.
I believe you're asking the difference between
public void myMethod(List list) {
list.add(new Object());
}
and
public List myMethod() {
List list = new ArrayList();
list.add(new Object());
return list;
}
Correct me if I'm wrong.
There is no rule to say which one is right. It all depends on how you wish to design your program. The latter method won't allow you to use existing Lists, so there may be performance issues to be considered.
You can also perform method chaining when returning values from a method, so sometimes you could take both a parameter and returning a value. A variation from this is a method that will use an existing List if it is passed as a parameter, but create a new List if the parameter is null. However this can be confusing to the caller.
First, there is no "pass by reference" in Java: the language is passing references by value (it is not the same thing).
The answer to your question is "it depends": passing an object as an argument to a method lets you reuse the same object in multiple invocations, while returning an object forces the method to supply a new or an existing object to the caller.
Consider this example: you are collecting data from several methods, and you need to put all the data in one list. You can have methods returning lists with their data
interface DataSource {
void supplyData(List<Data> list);
}
or you could pass these methods a list, and have them add their data to the same list:
interface DataSource {
List<Data> supplyData();
}
In the first case, you could loop through multiple data sources, passing them the same list:
List<Data> bigList = new ArrayList<Data>();
foreach (DataSource s : mySources) {
s.supplyData(bigList);
}
In the second case, you would need to get individual lists from the calls of supplyData, and put their content in a big list that you keep in your loop:
List<Data> bigList = new ArrayList<Data>();
foreach (DataSource s : mySources) {
List<Data> tmp = s.supplyData();
bigList.addAll(tmp);
}
In the second case each invocation creates a temporary list tmp that gets discarded after its content is added to the big list.
Note that passing an existing list is not necessarily a better solution - in fact, there are situations when you should avoid that.
For example, when you deal with externally supplied plug-ins, you should prefer the second strategy. Otherwise, a malicious implementation of the DataSource interface would be able to manipulate the common list in ways not expected by your program, such as adding its items ahead of everyone else's, removing items that came from other sources, examining items from other sources, and so on.
In addition to the answer from dasblinkenlight, which is correct (it depends), returning a list from the method also has advantages over passing a list as argument.
By passing a list as argument, the caller has the responsibility to choose the appropriate List implementation, to initialize it to the appropriate size, and to pass a list that is compatible with the algorithm of the called method. Often, the caller doesn't have the knowledge that is necessary to make the good choice.
Whereas if the called method returns a list, it can:
return Collections.emptyList() if nothing has to be returned
return a well-dimensioned ArrayList
return a subList or an unmodifable view or transformed view of a list it already has in memory, instead of making a copy
do anything with the list it creates without fearing that the list passed as argument is unmodifiable, or fixed-size, or already contains elements.
No difference, both are references to list object.
Personally I prefer that results are given by return and leave the arguments. But that might force you to create a new list, and that might cost you (performance, memory).
Returning allows for chaining.
As you are probably aware, everything in Java is pass-by-value.
Is there any difference in JAVA between passing an object as argument to a method or returning that object from the method?
No real difference as it is the reference that is being passed around in both cases.
Is it better to pass a List as an argument and fill it in the method or just allow the method to return a list?
Depends on your requirements I guess. Does the method require a list as input or can it construct it entirely by itself?
My guess is that it should be no difference since a reference is returned and nothing is copied?
Strictly speaking, this is not true as a copy of the reference is returned.
With 2 ArrayList, I was wondering if the best way from transforming the 1st one into a "copy" of the second one is to go like
myFirstArray.clear();
myFirstArray.addAll(mySecondArray);
or
myFirstArray = mySecondArray.clone();
What are the main differences between those two method, which on is preferrable and is there another "easier" or "cleaner" solution. Thanks for any tips
EDIT : I use this copy for replacing an Array of item im currently working with the one where I store the item I'll work with in the next loop. At the end of the loop I replace my currentArrayList with my futurArrayList and I clear my futurArraylist in order to add new item in it (i hope its clear enough)
The first one replaces the content of the list by another content. The second one creates another ArrayList instance, leaving the previous one untouched.
If the list is referenced by some other object, and you want this other object to be untouched, use the second one. If you want the other object to also have the new content, use the first one.
If nothing else referenced the list, it doesn't matter much. The second one will reduce the memory used in case you replace the content of a huge list by a few elements.
In java, though clone is ‘intended’ to produce a copy of the same object it is not guaranteed.
Clone comes with lots of its and buts. So my first advice is to not depend on clones.
By default, java cloning is ‘field by field copy’ i.e. as the Object class does not have idea about the structure of class on which clone() method will be invoked. So, JVM when called for cloning, do following things:
If the class has only primitive data type members then a completely
new copy of the object will be created and the reference to the new
object copy will be returned.
If the class contains members of any class type then only the object
references to those members are copied and hence the member
references in both the original object as well as the cloned object
refer to the same object because of that cloned object changes are visible in original also.
Use that:
List<Object> first = ...
ArrayList<Object> second = new ArrayList<>(first);
I also suggest that you do not use clone() at all. It's better to use a copy constructor or some factory method. Take a look at here.
Of course in your case, with the ArrayList, it will work as expected, you will end up with a copy of the references.
Guava, guava, guava!
final List copied = ImmutableList.copyOf(originalList);
I have a List<SomeBean> that is populated from a Web Service. I want to copy/clone the contents of that list into an empty list of the same type. A Google search for copying a list suggested me to use Collections.copy() method. In all the examples I saw, the destination list was supposed to contain the exact number of items for the copying to take place.
As the list I am using is populated through a web service and it contains hundreds of objects, I cannot use the above technique. Or I am using it wrong??!! Anyways, to make it work, I tried to do something like this, but I still got an IndexOutOfBoundsException.
List<SomeBean> wsList = app.allInOne(template);
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList.size());
Collections.copy(wsListCopy,wsList);
System.out.println(wsListCopy.size());
I tried to use the wsListCopy=wsList.subList(0, wsList.size()) but I got a ConcurrentAccessException later in the code. Hit and trial. :)
Anyways, my question is simple, how can I copy the entire content of my list into another List? Not through iteration, of course.
Just use this:
List<SomeBean> newList = new ArrayList<SomeBean>(otherList);
Note: still not thread safe, if you modify otherList from another thread, then you may want to make that otherList (and even newList) a CopyOnWriteArrayList, for instance -- or use a lock primitive, such as ReentrantReadWriteLock to serialize read/write access to whatever lists are concurrently accessed.
This is a really nice Java 8 way to do it:
List<String> list2 = list1.stream().collect(Collectors.toList());
Of course the advantage here is that you can filter and skip to only copy of part of the list.
e.g.
//don't copy the first element
List<String> list2 = list1.stream().skip(1).collect(Collectors.toList());
originalArrayList.addAll(copyArrayofList);
Please keep on mind whenever using the addAll() method for copy, the contents of both the array lists (originalArrayList and copyArrayofList) references to the same objects will be added to the list so if you modify any one of them then copyArrayofList also will also reflect the same change.
If you don't want side effect then you need to copy each of element from the originalArrayList to the copyArrayofList, like using a for or while loop. for deep copy you can use below code snippet.
but one more thing you need to do, implement the Cloneable interface and override the clone() method for SomeBean class.
public static List<SomeBean> cloneList(List<SomeBean> originalArrayList) {
List<SomeBean> copyArrayofList = new ArrayList<SomeBean>(list.size());
for (SomeBean item : list) copyArrayofList.add(item.clone());
return copyArrayofList;
}
I tried to do something like this, but I still got an IndexOutOfBoundsException.
I got a ConcurrentAccessException
This means you are modifying the list while you are trying to copy it, most likely in another thread. To fix this you have to either
use a collection which is designed for concurrent access.
lock the collection appropriately so you can iterate over it (or allow you to call a method which does this for you)
find a away to avoid needing to copy the original list.
Starting from Java 10:
List<E> oldList = List.of();
List<E> newList = List.copyOf(oldList);
List.copyOf() returns an unmodifiable List containing the elements of the given Collection.
The given Collection must not be null, and it must not contain any null elements.
Also, if you want to create a deep copy of a List, you can find many good answers here.
There is another method with Java 8 in a null-safe way.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.collect(Collectors.toList());
If you want to skip one element.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.skip(1)
.collect(Collectors.toList());
With Java 9+, the stream method of Optional can be used
Optional.ofNullable(wsList)
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList())
I tried something similar and was able to reproduce the problem (IndexOutOfBoundsException). Below are my findings:
1) The implementation of the Collections.copy(destList, sourceList) first checks the size of the destination list by calling the size() method. Since the call to the size() method will always return the number of elements in the list (0 in this case), the constructor ArrayList(capacity) ensures only the initial capacity of the backing array and this does not have any relation to the size of the list. Hence we always get IndexOutOfBoundsException.
2) A relatively simple way is to use the constructor that takes a collection as its argument:
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);
I was having the same problem ConcurrentAccessException and mysolution was to:
List<SomeBean> tempList = new ArrayList<>();
for (CartItem item : prodList) {
tempList.add(item);
}
prodList.clear();
prodList = new ArrayList<>(tempList);
So it works only one operation at the time and avoids the Exeption...
You can use addAll().
eg : wsListCopy.addAll(wsList);
re: indexOutOfBoundsException, your sublist args are the problem; you need to end the sublist at size-1. Being zero-based, the last element of a list is always size-1, there is no element in the size position, hence the error.
I can't see any correct answer. If you want a deep copy you have to iterate and copy object manually (you could use a copy constructor).
You should use the addAll method. It appends all of the elements in the specified collection to the end of the copy list. It will be a copy of your list.
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
List<String> copyList = new ArrayList<>();
copyList.addAll(myList);
just in case you use Lombok:
mark SomeBean with the following annotation:
#Builder(toBuilder = true, builderMethodName = "")
and Lombok will perform a shallow copy of objects for you using copy constructor:
inputList.stream()
.map(x -> x.toBuilder().build())
.collect(Collectors.toList());
subList function is a trick, the returned object is still in the original list.
so if you do any operation in subList, it will cause the concurrent exception in your code, no matter it is single thread or multi thread.
For example I have a reference to an mItem object of my List<mItem> mList collection. Is that possible to get a reference to mList using mItem?
The short answer is no. The items in a list don't know they are in a list. (Unless of course you add a reference to the list inside your mItem object.)
Short answer: no.
Adding an item to a list adds a reference to that object to the list. It does not affect the object itself.
You can check to see if an item is in a specific list, with mList.contains(mItem) which returns true if the item is in mList. Alternatively if you really need to, you could as others have suggested implement a version of List that informs each item that the list has a reference to that item. The overhead would be rather messy though.
Meta question: why do you need this functionality?
As jzd said... you can add a field to your mItem class (for example referenceHolder)
mItem(Object ref){
referenceHolder = ref;
}
class with List:
mList.add(new mItem(this));
something like that should work :) but its kind of weird ;), because normally "The items in a list don't know they are in a list. "
EDIT: and as said before - "One object should be a member of multiple collections" - so you would need to change type of my referenceHolder to some list or array, to make that able to happen.
EDIT2: that's just reference to object holding that list (if one object will have multiple lists, you wont be able to know witch of them is holding that sepecified mItem object), to reference to List itself you will need (as said in comment ;)) custom list implementation adding references to stored objects with this in add() method.
If you have a container for all you lists, you can call contains on each one to find which list the item is in.
Note: for a list you can have the same element multiple time, and across multiple list.
BTW: If you don't want duplicates in a list and don't care about the order of elements then a Set may be your best choice.