使用对象的n个克隆创建java集合

时间:2014-11-10 22:05:20

标签: java collections

在Java中,是否有一种单行方式来创建使用对象的 n 克隆初始化的集合?

我喜欢这相当于:

  • foo = vector<vector<int> >(10); c ++,创建10个不同的空向量
  • [ [] for i in range(10) ] Python,一个由10个不同的空数组组成的数组
  • Array.new(10) { [] } Ruby,与Python相同

在Java中,我只找到了

new ArrayList<ArrayList<Integer> >(Collections.nCopies(10, new ArrayList<Integer>()))

但是, 等同于其他示例,因为列表别名。

有没有办法在不使用for循环的情况下创建不同对象克隆的数组,最好不要诉诸external libraries

4 个答案:

答案 0 :(得分:7)

如果您使用的是Java 8,则可以使用其流:

Stream.generate(ArrayList<Integer>::new)
    .limit(10).collect(Collectors.toList());

Stream.generate()方法需要Supplier知道如何生成值并生成这些值的无限流(每个值都是通过再次调用供应商获得的,因此它们都是不同的,不像Collections.nCopies())。在流上放置limit(),然后将结果收集到列表中,从而生成不同条目的列表。

答案 1 :(得分:2)

对于那些想要传递构造函数参数的人(accepted answer和供应商提及here不可能) - 你可以使用以下内容(我这样做)不知道是否有更好的解决方案,但它至少满足了我的需求):

final List<MyObject> result = IntStream.range(0, n)
   .mapToObj(index -> new MyObject(...))
   .collect(Collectors.toList());

然而,您要将n替换为要填充列表的元素数量,并将MyObjectnew MyObject(...)分别替换为您的类型和ctor-call。

这将创建一个从0到n(n不包括)的整数流,映射每个&#34;索引&#34;到lambda-expr返回的任何对象。在mapToObj中,最后将流转换为包含nMyObjectimg实例的列表。

答案 2 :(得分:1)

即使引入了Java 8 Supplier,遗憾的是还没有像nCopies那样简洁的单行代码。说实话,我不知道为什么。 (虽然@DavidConrad表明Stream可以做到这一点。)

您可以自己创建一个,例如:

public static <E, L extends List<? super E>> L fill(
        L list, Supplier<E> sup, int n) {
    for(; n > 0; --n)
        list.add(sup.get());
    return list;
}

请致电:

List<List<Integer>> list = ArrayUtils.fill(
    new ArrayList<>, ArrayList<Integer>::new, 10
);

对于数组,有新方法Arrays#setAll

Integer[] oneToTen = new Integer[10];
Arrays.setAll(oneToTen, i -> i + 1);
List<Integer> asList = Arrays.asList(oneToTen);

但它是void方法,因此不能在单个语句中使用。 (个人评论:为什么Java API不能流畅?)

在Java 8之前,没有一种库方法可以做到这一点,创建一个更麻烦。由于clone受到保护,因此无法一般性地调用它。反思可以做到,但反思非常麻烦。

答案 3 :(得分:0)

  • vector<vector<int> > = new vector<vector<int> >(10); 在语法上不正确,但可以说你的意思是vector<vector<int> > foo(10);。您正在使用fill constructor 初始化容器大小,然后将每个元素初始化为value_type参数的副本(如果未指定任何内容,则初始化为默认构造函数)。这将使用循环。

  • [ [] for i in range(10) ]Array.new(10) { [] }只是在1行上进行循环并复制空列表类型结构。

如您所示,nCopies方法不相同,因为结果是不可变的,您不会创建副本(或克隆)。访问每个索引时使用对同一元素的引用。请参阅openjdk copies implementation以供参考。

java的一些困难是无法保证像C ++中那样的默认构造函数,并且语法与大多数脚本语言略有不同。这可能是一个很好的机会,可以花一点时间了解正在进行的工作,以确保您的解决方案不会做更多的工作。一些跟进问题要问自己:

  • 还有什么其他构造(除了你想到的循环)?我相信在某种程度上会有一个循环。
  • 你真的想要在外部ArrayList中初始化ArrayList对象吗?例如,您希望它们具有多大的尺寸?他们最初应该填充什么吗?你期望他们成长多大?它们需要是统一的尺寸还是有些更大/更小?懒惰地初始化这些列表是否有意义?

为了帮助回答这些问题,最好为您的用例编写自己的通用静态初始化程序。如果您的用例不同,在您获得简单案例后,可能会使您的解决方案更通用,以使用Factory Pattern来初始化您的内部列表。正如您所看到的,需要考虑很多问题,在简单的情况下,您最终可能会遇到类似的问题:

public static <T> List<List<T>> newListofLists(int outerSize, int innerSize, T value) {
    List<List<T>> outer = new ArrayList<List<T>>(outerSize);
    for (int i = 0; i < outer.size(); ++i) {
        List<T> inner = new ArrayList<T>(innerSize);
        outer.add(inner);
        for (int j = 0; j < inner.size(); ++j) {
            inner.add(value);
        }
    }
    return outer;
}

然后可以使用它来在一行中初始化列表,如:

List<List<Integer>> myList = newListofLists(10, 5, -1);