7

我有这个简单的可变参数方法,它划分列表中的每个项目:

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

我希望它传递数组的副本,但显然它以某种方式修改了我传入的数组,而不是它自己的本地副本:

$ 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]

所以我写了这个简单的测试程序:

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

它做了我所期望的,它在将对象数组传递给之前克隆了它f(因此有不同的对象标识符):

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

那么如何修改调用者fA数组而不是它自己的副本呢?

4

4 回答 4

15

看起来你在这里欺骗了自己:

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

由于os类型为Object,因此它被解释为可变参数数组的第一个元素。传入该方法的实际上是一个新Object[]元素,其单个元素是 your Object[]

如果您执行以下操作,它将打印相同的实例:

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

f方法需要制作数组本身的防御性副本,以保证调用者传入的数组不被修改。正如 arshajii 指出的那样,可变参数是最重要的数组参数,在给定参数列表时具有创建新数组的“奖励”行为。

无论如何,您可以Arrays.copyOf用来制作副本,委托给 (the less type-safe) System.arraycopy

于 2013-07-18T16:01:36.657 回答
3

varargs一种数组类型,但如果元素作为参数单独传递,则具有语法糖以允许动态创建数组。

也就是说,这两个签名是相同的:

static long f(long... xs) {
static long f(long[] xs) {

除了可以使用单独的元素而不是数组调用可变参数

当然,如果您绕过动态创建并自己创建一个数组以传入,则该数组将被修改。

于 2013-07-18T16:03:38.387 回答
1

那么 A 中的 f 如何修改调用者的数组而不是它自己的副本呢?

它没有自己的副本。它引用了调用者的数组。

于 2013-07-18T21:37:16.667 回答
-1

最后,数组是一个对象,所以你不要修改数组引用本身,而是修改它的内容,这是允许的。

于 2013-07-18T15:59:22.523 回答