如何创建包含N个对象的列表?

36

我正在尝试创建一个包含n个元素的对象列表。我希望以尽可能Java 8的方式来实现这一点。类似于在此处提出的C#问题: Creating N objects and adding them to a list

像这样:

List <Objects> getList(int numOfElements)
{

}

你能发一下你尝试过的代码吗? - John Joe
1
你实际想要实现什么? - Mritunjay
你是认真地想在问题发布后的2分钟内关闭它吗? - Marci-man
@Guy 这是用 C# 编写的 :D - Marci-man
5个回答

54
如果我理解您的问题正确:
List <Object> getList(int numOfElements){
     return IntStream.range(0, numOfElements)
              .mapToObj(Object::new) // or x -> new Object(x).. or any other constructor 
              .collect(Collectors.toList()); 
}

如果您需要 n 个相同的对象:
Collections.nCopies(n, T)

我发现我没有提交它...补交一下吧。 :) - holi-java
3
Collections.nCopies(n, T)将返回一个不可修改的List<T>。如果您需要获取可编辑的 List<T>,则应创建一个空列表,然后调用 addAll,并将 nCopies 的结果传递给它。List temp = new ArrayList<>(); temp.addAll(Collections.nCopies(n, T)); return temp; - afj88
2
@afj88的答案的简短版本:return new ArrayList<>(Collections.nCopies(n, T)); - Bonnev

45

您可以使用Stream.generate(Supplier<T>)与对构造函数的引用结合使用,然后使用Stream.limit(long)指定要构造多少个:

Stream.generate(Objects::new).limit(numOfElements).collect(Collectors.toList());    

对我来说,这比使用IntStream进行迭代更易读,并且更清楚地说明了意图,例如Alberto Trindade Tavares建议的那样

如果你想要在复杂度和内存使用方面表现更好,可以将结果大小传递给Stream.collect(Collector<? super T,A,R>)

Stream.generate(Objects::new).limit(numOfElements).collect(Collectors.toCollection(() -> new ArrayList<>(numOfElements)));

3
尽管正确,但要注意这种方法的并行化效果不如使用IntStream方法 - 因为这仍然是一个无限流(使用limit方法不能引入SIZED属性);除此之外还要点赞+1。 - Eugene
2
@Eugene:如果您希望获得最大并行性能,您应该使用Arrays.asList(IntStream.range(0, numOfElements).mapToObj(Objects::new).toArray(Objects[]::new)); - Holger
1
@Holger 我认为这与Spliterators底层有关?ArraySpliteratorRangeIntSpliterator更好吗? - Eugene
4
@Eugene:不,无论哪种情况下,您都有相同的Spliterator,因为源仍然是一个范围。但是,使用(子)大小流的toArray将立即分配最终数组,并让每个线程写入该数组的正确部分,与collect(Collectors.toList())对比,后者收集到本地列表并需要额外的合并步骤。此外,toList()使用的列表可能需要进行(多次)容量增加操作。也可以参见此评论 - Holger
1
我认为我们可以通过调用其构造函数来自定义对象,例如 Stream.generate(() -> new SomeClassName(someValue_01, ... , someValue_N)) .limit(size) .collect(Collectors.toList()); 或者我们甚至可以通过传递和自定义 supplier 来进行生成。 - kelgwiin

5

使用Java 8中的streams,等效于您提到的C#代码的实现如下(建议使用mapToObj,由@Eugene建议):

List <Objects> getList(int numOfElements)
{
  return IntStream.range(0, numOfElements)
                  .mapToObj(x -> new Objects())
                  .collect(Collectors.toList()); 
}

1
有点混乱。应该在这里使用的地图。 - Alberto Trindade Tavares
2
我也不太确定,但我认为他想将一些参数发送到他正在创建的对象构造函数 - 至少是 x,所以看起来应该是 .map(x -> new Objects(x)) 或更好的 .map(Objects::new) - Eugene
1
OP没有表明清楚。如果他想要将x作为构造函数的参数使用,你的答案是最好的(我已经点赞了)。 - Alberto Trindade Tavares

2

如果您不介意使用第三方依赖,则可以使用Eclipse Collections实现以下功能:

List<Object> objects = Lists.mutable.withNValues(10, Object::new);
Verify.assertSize(10, objects);

注意:我是Eclipse Collections的提交者。

2
为什么不保持简单?如果您想使用LINQ或Lambda,那肯定是可行的,但问题是它是否更易读和可维护。
List <Objects> getList(int numOfElements)
{
  List<Objects> objectList = new LinkedList<>();
  for(int i = 0; i <= numOfElements; i++)
  {
    objectList.add(new Object());
  }
  return objectList;
}

如果您坚持,这可以是lambda函数:
  return IntStream.range(0, numOfElements)
                  .mapToObj(x -> new Objects())
                  .collect(Collectors.toList()); 

感谢@Alberto Trindade比我更快地发布了这篇文章。


谢谢您的回复。我个人更喜欢老派的方法。但是使用Java8化的解决方案是必须的 :( - Marci-man

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接