0

I'd like to initialize a nested list without using a for-loop, the root list which is: cakeList will contain another ones eg.(100).

My code:

1. ArrayList<ArrayList<Integer>> cakeList = new ArrayList<>();
2. for (int i=0;i<100;i++) cakeList.add(new ArrayList<>());

I've tried:

import java.util.ArrayList;
public class Solution {
    public static void main(String[] args) {
        ArrayList<ArrayList<Integer>> cakeList = new ArrayList<>(100);
        cakeList.forEach(e-> new ArrayList<>());
        System.out.println(cakeList.size());
        cakeList.get(0);
    }
}

but as you maybe know, it complies, but throws an error al line #7 because cakeList is empty whe I try to cakeList.get(0).

IndexOutOfBoundsException: Index: 0, Size: 0

Thank you in advance!

  • the 2nd method using lamda going to give IndexOutOfBoundsException as cakeList is empty initially. Are you facing any issues while trying the first method? – prachi Apr 08 '21 at 16:40
  • ```cakeList.forEach(e-> new ArrayList<>());``` this method yes throws me a exception because cakeList is empty. – Heriberto Haydar Apr 08 '21 at 16:46
  • @HeribertoHaydar, `cakeList.forEach` does NOT throw `IndexOutOfBoundsException` when the `cakeList` is empty, just because there's no index here. Please clarify your issue. – Nowhere Man Apr 08 '21 at 16:49
  • As Alex Rudenko said there is no exception, do you want to initialize your `cakeList` without using a for loop, for example with a `Stream` ? – dariosicily Apr 08 '21 at 16:57
  • @AlexRudenko I rewrite the code.. you're right the exception is throws because the list is empty.. no because ```` cakeList.forEach(e-> new ArrayList<>()); ``` this code. :) – Heriberto Haydar Apr 08 '21 at 17:20

2 Answers2

0

It is convenient to use Stream::generate and Stream::limit to create predefined number of objects, so for the 2D list this would look as follows:

List<ArrayList<Integer>> list2D = Stream.generate(ArrayList<Integer>::new)
        .limit(100) // Stream<ArrayList<Integer>>
        .collect(Collectors.toList());

list2D.get(0).add(111); // 111 added to one entry

Also, IntStream with a fixed range may be used:

List<ArrayList<Integer>> list2D = IntStream.range(0, 100)
    .mapToObj(i -> new ArrayList<Integer>())
    .collect(Collectors.toList());

If it is important to have specific ArrayList<ArrayList> as a result, a collector Collectors.toCollection may be used:

ArrayList<ArrayList<Integer>> list2D = IntStream.range(0, 100)
    .mapToObj(i -> new ArrayList<Integer>())
    .collect(Collectors.toCollection(ArrayList::new));

There is also a method Collections.nCopies but it creates N copies of the same object which may have a side effect of applying a change in one element to all other "copy" elements in the list.

List<ArrayList<Integer>> cakeList = Collections.nCopies(100, new ArrayList<>());
cakeList.get(0).add(111); // 111 is added to "all" entries in cakeList
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • Thank you, I use this: ```` //Create Cakes IntStream.rangeClosed(1,N) .forEach(e->cakeList.add(new ArrayList<>())); ``` – Heriberto Haydar Apr 08 '21 at 17:21
  • @HeribertoHaydar, if you use Stream API, there's no need to use `forEach` this way to modify an external object. Create a stream of needed objects (here: empty `ArrayList`) and collect them into `List` or other collection as shown in the update. – Nowhere Man Apr 08 '21 at 17:41
0

Your initialization is fine, but post initialization, your lists are all empty, as you would expect docs (see Constructors) .

Per the methods section of the docs,

cakeList.get(0); // "Returns the element at the specified position in this list."

Since your list is empty, there is no element at the specified position. The only way to create an ArrayList that is non-empty is to use the constructor (see docs) which takes in a Collection. Example:

ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
numbers.get(0); //will return 1

Your goal of not wanting to use a for-loop for initialization is understandable, but see this post for the speed comparison of the two methods. Note the method call

cakeList.forEach(e-> new ArrayList<>());

is equivalent to

Iterator<T> iter = cakeList.iterator();
while (iter.hasNext()) {
  iter.next().add(new ArrayList<Integer>();
}

A conclusion you will reach is that there is no shortcut to filling arrays, it will take O(n) where n is your array size.

  • Thank you! you're right .. I'm not worry about speed it's more for writing a ++readable code, reducing for-loops =). I'm using this code: ` //Create Cakes IntStream.rangeClosed(1,N) .forEach(e->cakeList.add(new ArrayList<>())); ` – Heriberto Haydar Apr 08 '21 at 22:04