有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java ArrayList返回类型方法是返回ArrayList变量的内存地址还是它的副本?

我有一段代码:

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()方法是否返回diametersArrayList的内存地址(如果是,我是否需要像在构造函数中那样单独复制每个值),还是返回diametersArrayList中所有整数的副本

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

getDiameters()方法中,下面的返回语句是否是一个有效的语句,不会对安全性造成任何损害,而不是写“return new ArrayList<Integer>(diameters);

return diameters;

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


共 (4) 个答案

  1. # 1 楼答案

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

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

  2. # 2 楼答案

    如果你做了return diameters;,你的整个diameters数组列表将是可变的,因为你提供了一个直接指向它的链接。这意味着你可以从外部影响它的大小和内容

    执行new ArrayList<Integer>(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来实现

  3. # 3 楼答案

    getDiameters()将返回新创建的ArrayList的地址内存。它们不指向同一个地址内存。它会将直径元素复制到新的ArrayList。如果有多个线程,请使用Arraylist,出于安全考虑,您可以使用: Collections.synchronizedList(list);

  4. # 4 楼答案

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

    创建Integer的新ArrayList,让我们引用文档中的内容:

    public ArrayList(Collection c)

    Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

    Parameters: c - the collection whose elements are to be placed into this list Throws: NullPointerException - if the specified collection is null

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

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

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