2

I’ve written a piece of code that returns the result I want, but I’m not sure what exactly difference between these two variants of code is:

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

Alternatively:

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

And class from which I called a method is: Notes class (Notes.java file)

import java.util.ArrayList;

public class Notes {

    public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
        userRhythm.add("n8");
        return userRhythm;
    }
}

Namelly, when I add an item to ArrayList using addEight() method, and return it to the main, do I have to use statement with assignment operator like this:

userRhythm = Notes.addEight(userRhythm);

Or I could just call the method like this and it will add an item to the list.

Notes.addEight(userRhythm);

What is difference between these two statements, since in the end they both print same result? If I use only Notes.addEight(userRhythm); and not userRhythm = Notes.addEight(userRhythm); how does Java “knows” to which list an item was added?

Tobia Tesan
  • 1,938
  • 17
  • 29
Gitnik
  • 564
  • 7
  • 26
  • First notation is preferred for clarity purpose. If a method returns on object, it is advisable to get the return value from the method when you invoke that method. – Ravindra babu Jul 19 '15 at 16:32

9 Answers9

4

how does Java “knows” to which list an item was added?

It doesn't need to "know": you are looking at two references to the same single object in the heap.

Actual parameter userRhythm is an object reference passed to addEight by value, as is the case in Java (see also: Is Java "pass-by-reference" or "pass-by-value"?)

So:

  • In the first case, you are
    • Passing an object reference by value and
    • executing add on said object, which has side effects
  • in the second case, you are
    • Passing an object reference by value and
    • executing add on said object, which has side effects
    • Assigning the returned object reference to userRhythm, but they are the same, since the returned object reference is the actual parameter, so it doesn't do a great deal.

If you instead had:

public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
    return new ArrayList<String>();
}

then this would produce the expected result:

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

This would not:

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

Can you say why?


As a side note, it's not terribly useful to return anything, in fact. You could probably better off by making it a void method. You are dealing with references, hence you don't need to return things, you method's side effects are what you want.

Community
  • 1
  • 1
Tobia Tesan
  • 1,938
  • 17
  • 29
  • In the second example the return is assigned to userRhythm, that's why? But in the first example, what happened with returned value (new ArrayList())? Return created a new instance, but where did it go? – Gitnik Jul 19 '15 at 16:57
  • @KrausVonKranken 1. Yes, that's why. 2. Good question. After you have exited the function body, do you have *any* (remaining) references to that object? So *what* do you think happens to the object that was instantiated? – Tobia Tesan Jul 19 '15 at 16:59
  • It is considered to be garbage I suppose. – Gitnik Jul 19 '15 at 17:01
  • @KrausVonKraken: At which point what exactly happens? And, can you reverse this in any way? – Tobia Tesan Jul 19 '15 at 17:03
  • I'm not sure. I'm new to OOP. But considering all comments here, it is useless to return anything because it only returns reference. I can write method that just adds value to ArrayList, and to set its type to `void`. – Gitnik Jul 19 '15 at 17:10
  • @KrausVonKraken: at which point nothing "*happens*". The GC might decide to zap the instance now or in 30 minutes. Trick question :P No, you can't reverse this, if you are out of references, you are out of references. Yes, `void` makes sense in this case :) – Tobia Tesan Jul 19 '15 at 17:13
3

Suppose you had written it like this:

ArrayList<String> userRhythm = new ArrayList<String>();
ArrayList<String> userRhythm2 = Notes.addEight(userRhythm);
System.out.println(userRhythm2);

In that case, userRhythm and userRhythm2 would be references to the same ArrayList. There is only one list, but multiple references to it.

How can you resolve this?

Because you are modifying the parameter, you don't need to return it. You could rewrite your method as

public static void addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
}

This makes more sense semantically, because most add operations don't return anything at all.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
  • No. Java is pass by value. It copies the ArrayList. not pass by address. Hence in the original post, the two methods would not print the same result. – Samrat Dutta Jul 19 '15 at 16:36
  • 4
    @SamratDutta Java passes **references** by value. The method receives a different reference to the same ArrayList object. It doesn't copy objects when passing them as argument. – JB Nizet Jul 19 '15 at 16:37
  • @SamratDutta: it copies *the object reference*. – Tobia Tesan Jul 19 '15 at 16:44
  • Yes. I was mistaken. But it is still not clear to me. When I do the same thing with a String reference instead of arraylist, there is no change to the String, But using arraylist, the actual object changes. I dont quite understand how yet. Need to study about it. – Samrat Dutta Jul 19 '15 at 16:46
  • @SamratDutta that is because a `String` is an immutable object - it can't be changed once it is initialized. An `ArrayList` is mutable. – Glorfindel Jul 19 '15 at 16:47
  • 2
    TL;DR: You don't *change* your `String` (how could you, it's immutable), by appending `"foo"` to your `"bar"` `String` you are actually making your reference point to a *brand new* `String` `foobar`. At this point you are effectively dealing with two distinct objects. If you no longer have references to the original (e.g. `str = str + "bar"`) the GC will eventually take care of it. – Tobia Tesan Jul 19 '15 at 16:49
  • 1
    While this answer seems correct, I don't understand why it's not desirable to have more than one reference pointing to the same object. – fps Jul 19 '15 at 16:57
  • 1
    @FedericoPeraltaSchaffner it can be useful in some cases, but this should not be enforced by a method. The calling code can decide for itself if it needs something like `ArrayList userRhythm2 = userRhythm;`. – Glorfindel Jul 19 '15 at 16:58
  • 1
    @Glorfindel Agreed. Anyway, my point was that you should explain the reason in your answer. Otherwise it sounds like something you should never do because, well, because it is written somewhere that you shouldn't do it. – fps Jul 19 '15 at 17:03
2

Your method takes a list as argument, adds an element to that list, and returns that list. So your two code snippets are functionally equivalent.

They would not be equivalent if the method returned another list than the one it receives as argument. For example:

public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
    userRhythm.add("n8");
    return new ArrayList<String>();;
}

In that case, the first code snippet:

ArrayList<String> userRhythm = new ArrayList<String>();
userRhythm = Notes.addEight(userRhythm);
System.out.println(userRhythm);

would print [] (empty list), since it assigns the new, empty list returned by the method to userRhythmand prints it. Whereas the second snippet:

ArrayList<String> userRhythm = new ArrayList<String>();
Notes.addEight(userRhythm);
System.out.println(userRhythm);

would print [n8], since it ignores the empty list returned by the method, and prints the original list, that the method has modified.

Returning an argument from a method doesn't make much sense: the caller obviously already has a reference to that argument, so returning it is useless and confusing. You'd better make your method return void.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
1

The difference between the two is that in the first case, you are assigning the return value of the function to the userRythym variable, while in the second case, you discard the function's return value.

They act the same because the return value of the function is userRythym, so the assignment (in the first case) is just setting the variable to the value it already has.

schtever
  • 3,210
  • 16
  • 25
1

ArrayList is mutable, so you shouldn't return a value / reassign the arraylist reference (in your existing code).

If your method actually returns an ArrayList (List) taking an ArrayList (List) as an argument, then you could use the returned value.

example :

list = someClassInstance.toImmutableList(list); // toImmutableList() returns an immutable representation of list passed as argument 
TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • Why shouldn't the array list reference be returned by the method? – fps Jul 19 '15 at 16:55
  • @FedericoPeraltaSchaffner - because it makes no sense... You already have a reference pointing to the same instance.. returning another reference pointing to the same instance is not useful (and obviously will be less efficient) – TheLostMind Jul 19 '15 at 16:57
  • 2
    I know, my point was that you should state that in your answer ;) – fps Jul 19 '15 at 17:00
1

From a functional point of view, both methods do exactly the same: they add "n8" to the list passed as an argument.

The only difference is in the return statement: the first method does not return anything, while the second one returns a reference to the same list that was passed as an argument. While all this seems quite obvious, using the second option would allow you to do the following:

ArrayList<String> list = new ArrayList<>();
boolean hasNote = Notes.addEight(list).contains("n8"); // true

Whether this is useful to you or not, it will depend on your specific problem.

In general, it doesn't make sense to have more than one reference pointing to the same object. Consider the following:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Notes.addEight(list1);

System.out.println("list1 == list2 ? " + (list1 == list2)); // true, they point to 
                                                            // the same list
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true, since they 
                                                                      // not only contain 
                                                                      // the same 
                                                                      // elements, but
                                                                      // also point to the
                                                                      // same list

In this scenario, it doesn't seem to be very useful to have two references pointing to the same list, since it would be as doing this:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Notes.addEight(list1);
ArrayList<String> list3 = list1;

System.out.println("list1 == list2 ? " + (list1 == list2)); // true
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true
System.out.println("list1 == list3 ? " + (list1 == list3)); // true
System.out.println("list1.equals(list3) ? " + (list1.equals(list2))); // true

Now, it would be different if your Notes.addEight() method was implemented this way:

public static ArrayList<String> addEight(ArrayList<String> list) {
    list.add("n8");
    return new ArrayList<>(list); // NOTE THIS! A new ArrayList is being returned
}

If we invoked this new addEight() method, then things would be different:

ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Test.addEight(list1);
ArrayList<String> list3 = list1;

System.out.println("list1 == list2 ? " + (list1 == list2)); // false
System.out.println("list1.equals(list2) ? " + (list1.equals(list2))); // true
System.out.println("list1 == list3 ? " + (list1 == list3)); // true
System.out.println("list1.equals(list3) ? " + (list1.equals(list3))); true

As you see, now list1 and list2 are references that point to different objects (list1 == list2 is false), while list1.equals(list2) is true, since both lists contain the same elements in the same order (they are semantically equal).

However, list1 == list3 is true, since they actually point to the same list, due to the way list3 was declared (list3 was assigned to list1 by means of the assignment operator =).

fps
  • 33,623
  • 8
  • 55
  • 110
0
See the difference between these two implementations by creating new object in addEight method

import java.util.ArrayList;

public class Notes {

    public static ArrayList<String> addEight(ArrayList<String> userRhythm) {
        userRhythm = new ArrayList<String>();
        userRhythm.add("n8");
        return userRhythm;
    }
    public static void main(String args[]){
    ArrayList<String> userRhythm = new ArrayList<String>();
    userRhythm = Notes.addEight(userRhythm);
    System.out.println(userRhythm);


    ArrayList<String> userRhythm1 = new ArrayList<String>();
    Notes.addEight(userRhythm1);
    System.out.println(userRhythm1);

    }
}

Output:

[n8] -- If you get return value

[] - If you don't get the return value

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
0

how does Java “knows” to which list an item was added?

There is only 1 list in your code. It is passed by reference to your addEight() method, which then returns the reference.

aro_tech
  • 1,103
  • 7
  • 20
0

Namelly, when I add an item to ArrayList using addEight() method, and return it to the main, do I have to use statement with assignment operator [...] Or I could just call the method like this and it will add an item to the list.

If you pass an object reference to a method -- and every Java value is either a primitive (int, char, etc.) or an object reference -- then the method can alter any modifiable properties of the referred-to object, and those modifications can be observed via any reference to the object. Such a method may return a reference to that object as well, but that's an altogether separate issue. The current state of an object is always accessible via any reference to that object.

What is difference between these two statements, since in the end they both print same result? If I use only Notes.addEight(userRhythm); and not userRhythm = Notes.addEight(userRhythm); how does Java “knows” to which list an item was added?

The difference is simply that in one case you assign the reference returned by Notes.addEight() to the main() method's local variable userRhythm, and in the other case you don't. Because Notes.addEight() returns a copy of the reference passed to it, this has the effect of re-assigning the same value to userRythm that it already had. That's not useful, but not harmful, either.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I think I'm beginning to understand. Since it is just a reference passed to a method, I can use method to perform add(), but since the object is altered in the memory, I don't need to return anything, so the method could be void? – Gitnik Jul 19 '15 at 16:53
  • @KrausVonKraken: Correct. You *need* to get up to speed with the concept of object reference, side effects, etc. If you know C, it should be straightforward, if you don't know C (or C++), maybe you should also have your first fight with pointers and C++ references - *for example*, C++ lets you define a destructor with which you can make some interesting experiments (like having `std::cout << "Object was destroyed"` for destructor :) ) – Tobia Tesan Jul 19 '15 at 17:06
  • @Tobia Thanks Tobia, I will look into it. I only have experience in Matlab, and when compared to the simplest of Java program it looks like a very simple language. – Gitnik Jul 19 '15 at 17:27