42

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)
{

}
4
  • Can you post the code you tried ? Commented Jul 24, 2017 at 8:15
  • 1
    What actually you want to achieve? Commented Jul 24, 2017 at 8:16
  • Are you seriously trying to close the question within 2 minutes of its being posted? Commented Jul 24, 2017 at 8:17
  • @Guy it is in C# :D Commented Jul 24, 2017 at 8:20

5 Answers 5

64

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)
Sign up to request clarification or add additional context in comments.

3 Comments

I found I didn't up it... make up it. :)
Collections.nCopies(n, T) will return an immutable List<T> If you need to get an editable List<T> you should create an empty List and than call addAll passing the result of nCopies List<T> temp = new ArrayList<>(); temp.addAll(Collections.nCopies(n, T)); return temp;
A shorter version of @afj88's answer: return new ArrayList<>(Collections.nCopies(n, T));
49

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

5 Comments

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: if you want maximum parallel performance, you should use Arrays.asList(IntStream.range(0, numOfElements) .mapToObj(Objects::new) .toArray(Objects[]::new));
@Holger I assume that has to do with underneath Spliterators? And ArraySpliterator would be better than RangeIntSpliterator?
@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
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
7

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

4 Comments

Messed up a little. Map that should be used here.
I'm also not quite sure but i think he wants to send some param to the object constructor that he is creating - at least the x, so it looks like it should be .map(x -> new Objects(x)) or better .map(Objects::new)
OP didn't make clear. If he wants to use x as parameter for constructor, your answer is the best one (have upvoted that)
The easiest way to write this is still a for loop.
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.

2 Comments

Thanks for your reply. I prefer the more old-school approach myself. But using Java8ified solution is a requirement :(
Always use ArrayList instead of LinkedList as a habbit. This solution is more efficient than streaming if you initialize the list with numOfElements as capacity. Then you only need to allocate once in this loop. Very tight, very efficient, one object created.
3

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.