【问题标题】:Does the ArrayList return type method return the memory address of the ArrayList variable or a copy of it?ArrayList 返回类型方法是返回 ArrayList 变量的内存地址还是它的副本?
【发布时间】:2020-05-07 16:01:43
【问题描述】:

我有一段代码:

public class Check {
    private ArrayList<Integer> diameters;       // Array of diameters

    public Check(int num) {
        diameters = new ArrayList<>(num);       // Create an ArrayList object.
        for(int i = 0; i < num; i++) {          // Initialize the ArrayList.
            diameters.add(i, i + 1);
        }       
    }

    public ArrayList<Integer> getDiameters() {
        return new ArrayList<Integer>(diameters);
    }
}

getDiameters() 方法是否返回直径 ArrayList 的内存地址(如果是,我需要像在构造函数中那样单独复制每个值)还是返回直径中所有整数的副本ArrayList?

getDiameters()方法的返回值和变量diameters是否指向同一个内存位置?

getDiameters() 方法中的以下 return 语句是否会是一个有效的语句,而不是写“return new ArrayList&lt;Integer&gt;(diameters);”而不会对安全性造成任何损害?

return diameters;

如果我写上上述声明,会面临哪些安全问题(如果有)?

【问题讨论】:

标签: java arraylist reference return-type


【解决方案1】:

如果您使用return diameters;,您的整个diameters ArrayList 将是可变的,因为您直接提供了指向它的链接。这意味着您可以从外部影响它的大小和内容。

当您执行 new ArrayList&lt;Integer&gt;(diameters) 时 - 您正在提供 ArrayList 的副本,其中包含复制的值(通过引用复制,因此基本上与原始集合中的对象相同)。

对于像 Integer 这样的原始包装器 - 它是安全的,但如果您在集合中传递可变对象 - 它的更改会更改原始 ArrayList 内容。

简单示例:


public class Arrayz {
  public static void main(String[] args) {
    List<Integer> array1 = new ArrayList<>();
    array1.add(new Integer(1));
    array1.add(new Integer(2));
    List<Integer> array2 = new ArrayList<>(array1);
// you can't mutate Integer - it's immutable
    Integer first = array2.get(0); 
    System.out.println(array1);
    System.out.println(array2);

    List<Nom> array3 = new ArrayList<>();
    array3.add(new Nom(1));
    array3.add(new Nom(2));
    List<Nom> array4 = new ArrayList<>(array3);
    Nom third = array4.get(0);
// Nom is muttable - this will affect initial data as the Object itself mutated
    third.a = 88; 
// this will not - you replaced the Object in copied array only
    array4.set(1, new Nom(33)); 
    System.out.println(array3);
    System.out.println(array4);
  }
}

public class Nom {
  public int a;
  Nom(int a) {
    this.a = a;
  }

  @Override
  public String toString() {
    return "" +a;
  }
}
//////////// output:
[1, 2] // array1
[1, 2] // array2
[88, 2] // array3
[88, 33] // array4

通常为了保护您需要传递带有不可变对象的不可变集合的所有内容。 首先可以通过使用com.google.common.collect.ImmutableList.of 来实现。

【讨论】:

    【解决方案2】:
    public ArrayList<Integer> getDiameters()
    {
        return new ArrayList<Integer>(diameters);
    }
    

    创建一个新的整数数组列表,让我们从文档中引用:

    公共数组列表(集合 c)

    构造一个包含指定集合元素的列表, 按照集合的迭代器返回的顺序。

    参数: c - 将其元素放入此列表的集合 抛出: NullPointerException - 如果指定的集合为空

    https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#ArrayList-java.util.Collection-

    现在,您已将Collection 传递给构造函数,该构造函数又生成一个 ArrayList,它与您的数据成员相似但不同。如果没有很好的理由,不建议这样做,因为直径已经包含了你拥有的元素。

    关于安全性,我看到的唯一安全问题是使用直径的代码将对实际的ArrayList 具有写入操作权限。如果您打算阻止这种情况,请不要返回实际的ArrayList。您可以clone 它或创建另一个ArrayList(就像您所做的那样),或者您可以(在我看来这是最优雅的解决方案)调用unmodifiableList 以返回您的Collection 的只读版本。

    【讨论】:

      【解决方案3】:

      getDiameters() 将返回新创建的 ArrayList 的地址内存。它们不指向相同的地址存储器。它将直径元素复制到新的 ArrayList。如果您有许多线程使用 Arraylist,为了安全起见,您可以使用: Collections.synchronizedList(list);

      【讨论】:

        【解决方案4】:

        对于对象,Java 将引用传递给对象,因此该方法不会返回列表的深层副本。

        至于安全问题,这意味着如果用户操作return diameters返回的对象,这些更改也会影响Check对象引用的对象,因为它们是同一个对象,这可能是一个问题。

        【讨论】:

          猜你喜欢
          • 2014-04-23
          • 1970-01-01
          • 2014-05-12
          • 1970-01-01
          • 2013-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多