Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Varargs method modifies caller's array instead of its own copy?

I have this simple varargs method which divides each item in the list:

import java.util.*;
class A {
    static long f(long... xs) {
      Arrays.sort(xs);
      long y = 100000000;
      for (int i = xs.length - 1; i >= 0; i--)
        y /= xs[i];
      return y;
    }
    static {
        System.out.println(f(5,2,6,3,9,3,13,4,5));
        long[] xs = new long[]{5,2,6,3,9,3,13,4,5};
        System.out.println(Arrays.toString(xs));
        System.out.println(f(xs));
        System.out.println(Arrays.toString(xs));
    }
}

I'd expect it to pass a copy of the array, but apparently it's somehow modifying the array I pass in, instead of its own local copy:

$ javac A.java && java A
79
[5, 2, 6, 3, 9, 3, 13, 4, 5]
79
[2, 3, 3, 4, 5, 5, 6, 9, 13]

So I wrote this simple test program:

class B {
    static void f(Object... os) {
        System.out.println(os);
    }
    static {
        Object os = new Object[]{1,2,3};
        System.out.println(os);
        f(os);
    }
}

And it does what I expect, it clones the object array before passing it into f (hence different object identifiers):

$ javac B.java && java B
[Ljava.lang.Object;@1242719c
[Ljava.lang.Object;@4830c221

So how then is f in A modifying the caller's array instead of its own copy?

like image 379
Dog Avatar asked Jul 18 '13 15:07

Dog


People also ask

What is a varargs method in Java?

A method that takes a variable number of arguments is a varargs method. Prior to JDK 5, variable-length arguments could be handled two ways. One using overloaded method (one for each) and another put the arguments into an array, and then pass this array to the method. Both of them are potentially error-prone and require more code.

Why do I need to use two different names for varargs?

Since there are two possibilities, it causes ambiguity. Because of this, sometimes you may need to use two different method names instead of overloading the varargs method. What is varargs?

How to use varargs with 3 arguments in Python?

In order to define vararg, ... (three dots) is used in the formal parameter of a method. A method that takes variable number of arguments is called a variable-arity method, or simply a varargs method. As you can clearly see, you had to overload sumNumber () method to make it work for 3 arguments.

Can a method have more than one varargs parameter?

A method can have only one varargs parameter. In the above program, the compiler gets confused if you try to invoke the test () method even though test () methods are overloaded and accepts different number of arguments. The compiler doesn’t know which method to call.


1 Answers

It looks like you've tricked yourself here:

Object os = new Object[]{1,2,3};
System.out.println(os);
f(os);

Since os is typed as Object, it gets interpreted as the first element of the varargs array. What gets passed into the method is actually a new Object[] whose single element is your Object[].

If you do the following, it will print the same instance:

Object[] os = new Object[]{1,2,3};
System.out.println(os);
f(os);

The f method will need make a defensive copy of the array itself in order to guarantee that an array passed in by the caller isn't modified. As arshajii points out, varargs are foremost array parameters, with the "bonus" behavior of creating a new array when given an argument list.

Anyway you can use Arrays.copyOf to make the copy, which delegates to (the less type-safe) System.arraycopy.

like image 121
Paul Bellora Avatar answered Sep 29 '22 13:09

Paul Bellora