34

I am trying to create a list of objects with n elements. I am trying to do this in the most java 8 way as possible. Something similar to the question asked for c# here : Creating N objects and adding them to a list

Something like this:

List <Objects> getList(int numOfElements)
{

}
Mridang Agarwalla
  • 43,201
  • 71
  • 221
  • 382
Marci-man
  • 2,113
  • 3
  • 28
  • 76

5 Answers5

51

If I got your question right:

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()); 
}

If you want the same object n times:

Collections.nCopies(n, T)
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • I found I didn't up it... make up it. :) – holi-java Jul 24 '17 at 19:09
  • 3
    `Collections.nCopies(n, T)` will return an immutable `List` If you need to get an editable `List` you should create an empty List and than call addAll passing the result of nCopies `List temp = new ArrayList<>(); temp.addAll(Collections.nCopies(n, T)); return temp;` – afj88 Oct 16 '17 at 17:08
  • 1
    A shorter version of @afj88's answer: ```return new ArrayList<>(Collections.nCopies(n, T));``` – Bonnev Oct 04 '21 at 18:42
44

You could use Stream.generate(Supplier<T>) in combination with a reference to a constructor and then use Stream.limit(long) to specify how many you should construct:

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

At least to me, this is more readable and illustrates intent much more clearly than using an IntStream for iteration does as e.g. Alberto Trindade Tavares suggested.

If you want something which performs better in terms of complexity and memory usage, pass the result size to Stream.collect(Collector<? super T,A,R>):

Stream.generate(Objects::new).limit(numOfElements).collect(Collectors.toCollection(() -> new ArrayList<>(numOfElements)));
errantlinguist
  • 3,658
  • 4
  • 18
  • 41
  • 3
    while correct, note that this will parallelize worse then a `IntStream` approach - because this is still an infinite stream (limit will not introduce the `SIZED` property); otherwise +1 from me. – Eugene Jul 24 '17 at 08:44
  • 2
    @Eugene: if you want maximum parallel performance, you should use `Arrays.asList(IntStream.range(0, numOfElements) .mapToObj(Objects::new) .toArray(Objects[]::new));` – Holger Jul 24 '17 at 11:49
  • 1
    @Holger I assume that has to do with underneath Spliterators? And `ArraySpliterator` would be better than `RangeIntSpliterator`? – Eugene Jul 24 '17 at 12:58
  • 4
    @Eugene: no, you have the same spliterator in both cases, as the source is still a range. But `toArray` with a (sub)sized stream will allocate the final array immediately and let each thread write into the right part of that array, in contrast to `collect(Collectors.toList())` which collects into local lists and needs additional merging steps. Also, the lists used by `toList()` may need (multiple) capacity increase operations. See also [this comment](https://stackoverflow.com/questions/28782165/why-didnt-stream-have-a-tolist-method#comment45848538_28782165) – Holger Jul 24 '17 at 15:27
  • 1
    I would say that we can custom our object by calling its constructor i.e `Stream.generate(() -> new SomeClassName(someValue_01, ... , someValue_N)) .limit(size) .collect(Collectors.toList());` Or even we can pass through and custom _supplier_ to make the generation – kelgwiin May 14 '20 at 09:39
5

An equivalent implementation of the C# code you mentioned in Java 8, with streams, is the following (EDIT to use mapToObj, suggested by @Eugene):

List <Objects> getList(int numOfElements)
{
  return IntStream.range(0, numOfElements)
                  .mapToObj(x -> new Objects())
                  .collect(Collectors.toList()); 
}
Eugene
  • 117,005
  • 15
  • 201
  • 306
Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
3

Why not keep it simple?? If you want to use LINQ or a Lambda it definetly is possible but the question is if it is more readable or maintainable.

List <Objects> getList(int numOfElements)
{
  List<Objects> objectList = new LinkedList<>();
  for(int i = 0; i <= numOfElements; i++)
  {
    objectList.add(new Object());
  }
  return objectList;
}

If you insist this can be the lambda:

  return IntStream.range(0, numOfElements)
                  .mapToObj(x -> new Objects())
                  .collect(Collectors.toList()); 

Creds to @Alberto Trindade since he posted it faster than me.

Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
Nico
  • 1,727
  • 1
  • 24
  • 42
  • Thanks for your reply. I prefer the more old-school approach myself. But using Java8ified solution is a requirement :( – Marci-man Jul 24 '17 at 08:47
2

If you don't mind a third-party dependency, the following will work with Eclipse Collections:

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

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44