有 Java 编程相关的问题?

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

java集合。不创建列表副本的nCopies

我已经创建了List个默认大小的列表。因此,我使用set方法将数据添加到列表中

我的代码:

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test2 {
public static void main(String args[]) {
    List<List<String>> keys = new ArrayList<>(Collections.nCopies(3, new ArrayList<>()));
    List<String> values = keys.get(2);
    values.add("Hello");
    values.add("One Two");
    keys.set(2, values);
    List<String> tempList = keys.get(1);
    tempList.add("Now adding at 1");
    keys.set(1, tempList);
    System.out.println("Position 1 " + keys.get(1).toString());
    System.out.println("Position 2 " + keys.get(2).toString());
}
}

我的输出:

Position 1 [Hello, One Two, Now adding at 1]
Position 2 [Hello, One Two, Now adding at 1]

它为什么这么做?第一个数据被附加到第二个数据上了吗?我做错了什么


共 (1) 个答案

  1. # 1 楼答案

    List<List<String>> keys = new ArrayList<>(Collections.nCopies(3, new ArrayList<>()))
    

    在这个声明中,它并没有创建三个新的ArrayList<>实例。它创建一个实例,并创建一个列表,其中包含对同一实例的三个引用。因此,列表列表中的每个位置都是对同一ArrayList实例的引用

    解决这个问题最简单的方法就是使用for循环:

    for(int i = 0; i < numCopies; i++) {
        keys.add(new ArrayList<>());
    }
    

    使用Java 8的流式API的等效解决方案:

    IntStream.range(0, n).mapToObj(i -> new ArrayList<String>()).forEach(keys::add);
    

    或者你可以一次创建keys,就像这样:

    List<List<String>> keys = IntStream.range(0, numCopies)
                                       .mapToObj(i -> new ArrayList<String>())
                                       .collect(Collectors.toList());
    

    我们使用i -> new ArrayList<String>()而不是ArrayList<String>::new,因为有一个ArrayList<T>构造函数,它接受一个整数参数作为初始容量,这意味着它最终实际上是new ArrayList<String>(0)new ArrayList<String>(1),等等,这不是我们想要的

    一个更优雅的选择是使用Stream.generate

    Stream.generate(ArrayList<String>::new).limit(n);