I have a question. What is wrong with regards to the below code:
ArrayList tempList2 = new ArrayList();
tempList2 = getXYZ(tempList1, tempList2);
//method getXYZ
getXYZ(ArrayList tempList1, ArrayList tempList2) {
//does some logic and adds objects into tempList2
return tempList2;
}
The code will get executed but it seems by passing tempList2 to the getXYZ method argument, it is doing object recycling.
My question is, Is recycling the tempList2 arraylist object correct?
My question is, Is recycling the tempList2 arraylist object correct?
I don't quite know what you mean by "recycling". This doesn't appear to be a case where the application is recycling objects in an attempt to avoid allocating new objects. (That is the normal meaning of "recycling" in Java.)
If getXYZ is called multiple times with the same tempList2 object, then this is simply a way of aggregating stuff into a single list. The fact that getXYZ returns an ArrayList leaves open the possibility that the method implementation may be changed to return a different ArrayList instance. That's not a problem per se, but it might be if the caller doesn't assign the result of the call appropriately.
If getXYZ is only called once for any given tempList2 object, then this looks a bit strange.
In summary, this code looks a bit fishy, and is fragile if someone changes the implementation of getXYZ. However, it is not down-right wrong, and there may be some good reason (or historical reason) for doing things this way that is not apparent in the small chunks of code you included in the question.
EDIT - in response to this comment (inlined to make it readable)
Actually, the reason for the above code is, I wanted to avoid creating two arraylist objects. For ex: the conventional method would be
ArrayList tempList2 = new ArrayList();
tempList2 = getXYZ(tempList1);
//method getXYZ
getXYZ(ArrayList tempList1) {
ArrayList tempList = new ArrayList();
//INSTANTIATED ONLY ONCE
//does some logic and adds objects into tempList
return tempList;
}
The real conventional way of doing this would be:
ArrayList tempList2 = getXYZ(tempList1);
or
ArrayList tempList2;
// some intermediate lines
tempList2 = getXYZ(tempList1);
Neither of these require creating the unnecessary ArrayList instance of your approach, and neither require passing 2 ArrayList instances into the getXYZ method.
There's really no such concept as "object recycling" here. Nothing's being garbage collected, if that's what you're interested in... at least, not if there are no lines like this:
tempList2 = new ArrayList();
within getXYZ().
The reference to the ArrayList is passed into the method and then returned. That's all. It's just a reference - it's not the object itself.
Now you could say it's slightly odd for the method to return the ArrayList reference which was passed into it... usually that's used for fluent interfaces, but it doesn't seem to be the case here.
You're passing the value of the tempList2 reference. The object itself isn't copied. So when the passed-in tempList2 is modified, the caller sees the changes. This is also called call-by-sharing
So there's nothing wrong, once you understand what's going on.
Related
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.
I got a question about constructing a new ArrayList based on an already existing list, I need it to reverse a List. I do not need to deep clone the elements, since I only inspect their values and do not change them.
My old, working, code, it seems a bit hackish to me, so I think I had problems with it in the past:
Collections.sort(invoiceWordGroups, new WordGroup.WordGroupComparator());
insertAttributes(topAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
Collections.reverse(invoiceWordGroups);
insertAttributes(bottomAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
My new code, I will test it aswell ofcourse, but even then some errors might remain to be in there, if my basic concepts are not oK. So would this have the same behaviour?
Collections.sort(invoiceWordGroups, new WordGroup.WordGroupComparator());
List<WordGroup> invoiceWordGroupsReverse = new ArrayList<>(invoiceWordGroups);
Collections.reverse(invoiceWordGroupsReverse);
insertAttributes(topAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
insertAttributes(bottomAttributeWords, invoiceWordGroupsReverse, templateId, templateAttributeManager, invoiceMarginDataVAT);
The question is about invoiceWordGroups, which is of type List<WordGroup>. The reason I am changing it is because I will need to use the lists multiple times now, and constantly reversing it does definately not seem to be a good option.
If you check the Java source code the copy constructor for ArrayList creates a new list object which copies (hence the name copy constructor) the internal array (but even though it's a copy of the original array it still points to the same elements!!!). This internal array object is not shared therefore and it is the object which is responsible for what a given list object actually stores and in what order:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
So reversing one list won't affect the order of the other. Same goes for adding/deleting elements from one list or the other.
You said that those are read-only objects then it will be ok but remember that even though both lists are different objects they still point to the same elements, so changing the state of object X in list1 will also be shown when accessing that object X with list2.
I have deep nested structures, and methods like "remove(<Something>)", "contains(<Something>)" etc. rely on access to the original reference in order to remove it etc. (a copy of the reference won't do).
How have people best worked around this so they can still conveniently add, remove, test for etc. the object they want, within different arbitrary methods and constructors etc., without adding any unnecessary complexity or any unnecessary loss in performance?
Methods like remove and contains work fine with pass by value. The reason is that the even though the references are copied, they copy has the same value of the original. Just look at the Collections API. If you do something like (psuedocode)
List list = new List();
list.add(object1) // assume you have an object1 reference
and then you do
myRemove(list, object1);
both list and object 1 are passed by value, so in the myRemove method they are copies of the original reference. If in myRemove you do
list.remove(object1);
the object is still removed from the list no problem. Furthermore, since the list and object1 references in both scopes point to the same underlying objects, the list reference in the calling scope refers to the list that has the object removed.
The only time you would need pass by reference semantics is if you want to modify a reference in a method and have the modification apply in the scope that called the method.
So if you want to do
List myList = new List();
changeList(myList);
and have changeList change where myList points in the calling scope, it wont work without some trickery. The trickery is called double indirection. Basically, you create an object that has an instance of what you want to be able to access with pass by reference semantics, and you pass the container object around.
So
class MyContainer {
List list
}
now you can pass an instance of MyContainer into a method, and change the list value, and in the calling scope where the list points will be changed. Note that you are not doing anything special here, everything is still pass by value.
How have people best worked around this so...
By use of member fields (for working with references, not copies) and by use of inheritance and interfaces (for handling nested structures).
I've searched but I did not find a good answer.
I've used an ArrayList object.I created an instance object, (example object X), I used that ArrayList as a parameter on constructor object X, but everytime I created an instance of object X, the ArrayList included the old values, didn't create a new ArrayList.
I need to use add method like arraylist. This is the code:
public DataPacket(int hop, int TTL, ArrayList onetimevisit){
this.hop = hop;
this.TTL = TTL;
this.visited = onetimevisit;
}
in other looping process, DataPacket will meet object NodeRandom:
public NodeRandom(int id){
this.id = id;
}
then DataPacket will add the id of NodeRandom.
Is there an Object in Collection isn't static?
I'll take a guess that your issue has to do with an incorrect assumption about how java passes objects as parameters in method calls. Check out this answer: Is Java "pass-by-reference" or "pass-by-value"?
Short answer:
change
this.visited = onetimevisit;
to
this.visited = new ArrayList (onetimevisit);
Longer answer:
ArrayLists are not necessarily static. I think you're incorrectly inferring that the ArrayList must somehow have been set to static from the fact that there is only one copy of the ArrayList when you pass it in the way you've passed it. The thing to understand is that when you pass an object in Java (an ArrayList, for example), you're passing a reference to the object. A reference is something akin to a C-style pointer with the distinction that pointer arithmetic and such is not allowed. When you call a method and pass an object, the called method just gets a copy of the reference and not a copy of the object. Likewise, when you use the = operator to assign one object to another, you're only assigning the references to equal each other, and there is still only one copy of the object. In your code, both this.visited and onetimevisit are references that come to point to the same object in memory.
On the other hand, ArrayList has something that is somewhat akin to a copy constructor. This constructor, called in my sample code above, creates a shallow copy of the given ArrayList, which seems to be what you want. It is worth noting that an ArrayList does not copy the objects added to it (it stores references to them), so perhaps what you really need is to create copies of the objects as well. This would be done by calling their copy constructors (if they allow copying by providing such a constructor) before inserting them into the ArrayList.
If you want a new ArrayList() you have to create one, it won't do it automagically.
So I have an ArrayList in java. And what I'm doing is creating a new list with updated values. But I want to put those new values into the original ArrayList. This is important because I'm passing the original arraylist reference to an object that I no longer have access to, but I need to keep its contents up to date. Is there any way to make java copy the contents into a current reference? I hope that makes sense.
Also, I'd rather not clear the original arraylist and then loop through it pushing the new contents into it.
I need to do something like this:
ArrayList a;
ArrayList *b = a;
//pass b (the pointer) to something
ArrayList c;
*b = c;
I really hope I got that (pseudo) c++ code correct or I'll look pretty stupid =P
I can see how I'm not being clear, it's kind of complicated (this is in android so it's across a couple activities) so let me see if I can get my point across better.
Class1{
ArrayList a;
method1(){
a = Class2.getA();
method_that_uses_a(a);
}
}
Class2{
ArrayList getA(){
ArrayList a = new ArrayList
a = getDataFromWebsite();
return a;
}
Class1's method1() gets called periodically by another portion of code. But I need the reference to a stay the same, but the contents to change to the new contents.
I think your question is unclear, what do you mean by
"And what I'm doing is creating a new list with updated values. But I want to put those new values into the original ArrayList. This is important because I'm passing the original arraylist reference to an object that I no longer have access to, but I need to keep its contents up to date. Is there any way to make java copy the contents into a current reference? I hope that makes sense."
When you do
List a = new ArrayList
List b = a
you have one ArrayList object, and two references to the object, a and b.
Also note that there is an addAll method that you can use to add members of one collection to another collection. Note that I believe addAll does not do a deep copy, so if you use it both lists have copies of the same reference. So if list a has references a1, a2, a3, and you do b.addAll(a), b now has references to the objects that a1...a3 point to...
The array classes all expose public clone() methods, however, so if a
shallow copy of the array is sufficient then
return (ElementType[]) myArray.clone();
Makes for a nice, simple, easy to read paradigm. If "ElementType" is a
primitive type then there is no distinction between shallow and deep
copying, and if it is immutable then performing a deep copy would be
wasteful. In some other circumstances a shallow copy is what you would
want anyway (you want the receiver to be able to mutate the returned
array elements with effects visible to the host object).
If you find that you really do want to make a deep copy of the array
then you might want to consider whether your design can be improved; it
is not necessarily so, but I wouldn't be surprised.
In Java as I hope you;ve found out by now, all variables are references. Among other things this means that unless they are assigned to an object they don't 'point' at anything. You need to write:
ArrayList a = new ArrayList();
or a doesn't 'point to' an actual object - it's just null.
If you write:
ArrayList a = new ArrayList();
ArrayList b = a;
modify(b);
ArrayList c = b;
then there is only one ArrayList. All the variables a, b and c refer to it. Any modifications done in the 'modify' method apply to it, and will be reflect in the state of a, b and c.
You're C++ code says this:
ArrayList a; //make arraylist
ArrayList *b = a; //set b to point to same arraylist as a
//pass b somewhere
ArrayList c; //make arraylist
*b = c; //variable b in THIS PROGRAM now points to c. arraylist is unchanged.
You want to update the arraylist, not the pointer to it, as that pointer only 'exists' in the current context. The code you passed the arraylist to doesn't give a darn if a pointer back in who-knows-where now points to the same arraylist that its using. It's the actual object the other code cares about. To update that object, you can just call methods on it, like a.add(bar)
BUT there's more to it. If you call a function you don't control (now known as foo) and pass it an arraylist, that's all fine. But if you want to update it from the code calling foo, you run into issues. Imagine if an object you were working with could change randomly at any time. All sorts of bad stuff could happen. If you really need this capability (and we can help you judge if you do), look into the topic of threading.