Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8: How to turn a list into a list of lists using lambda

I'm trying to split a list into a list of list where each list has a maximum size of 4.

I would like to know how this is possible to do using lambdas.

Currently the way I'm doing it is as follow:

List<List<Object>> listOfList = new ArrayList<>();

final int MAX_ROW_LENGTH = 4;
int startIndex =0;
while(startIndex <= listToSplit.size() )    
{
    int endIndex = ( ( startIndex+MAX_ROW_LENGTH ) <  listToSplit.size() ) ? startIndex+MAX_ROW_LENGTH : listToSplit.size();
    listOfList.add(new ArrayList<>(listToSplit.subList(startIndex, endIndex)));
    startIndex = startIndex+MAX_ROW_LENGTH;
}

UPDATE

It seems that there isn't a simple way to use lambdas to split lists. While all of the answers are much appreciated, they're also a wonderful example of when lambdas do not simplify things.

like image 499
Lucas T Avatar asked Aug 22 '17 12:08

Lucas T


People also ask

How do you make a List of lists in Java?

Given below is the simplest way to create a list of lists in Java: For String: List<List<String>> listOfLists = new ArrayList<>(); That's it.

Can we have List of List in Java?

We can look at the “List of Lists” data structure as a two-dimensional matrix. So, if we want to group a number of List<T> objects, we have two options: Array-based: List<T>[]


4 Answers

If you REALLY need a lambda it can be done like this. Otherwise the previous answers are better.

    List<List<Object>> lists = new ArrayList<>();
    AtomicInteger counter = new AtomicInteger();
    final int MAX_ROW_LENGTH = 4;
    listToSplit.forEach(pO -> {
        if(counter.getAndIncrement() % MAX_ROW_LENGTH == 0) {
            lists.add(new ArrayList<>());
        }
        lists.get(lists.size()-1).add(pO);
    });
like image 128
Jotunacorn Avatar answered Nov 15 '22 19:11

Jotunacorn


Try this approach:

static <T> List<List<T>> listSplitter(List<T> incoming, int size) {
    // add validation if needed
    return incoming.stream()
            .collect(Collector.of(
                    ArrayList::new,
                    (accumulator, item) -> {
                        if(accumulator.isEmpty()) {
                            accumulator.add(new ArrayList<>(singletonList(item)));
                        } else {
                            List<T> last = accumulator.get(accumulator.size() - 1);
                            if(last.size() == size) {
                                accumulator.add(new ArrayList<>(singletonList(item)));
                            } else {
                                last.add(item);
                            }
                        }
                    },
                    (li1, li2) -> {
                        li1.addAll(li2);
                        return li1;
                    }
            ));
}
System.out.println(
        listSplitter(
                Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
                4
        )
);

Also note that this code could be optimized, instead of:

new ArrayList<>(Collections.singletonList(item))

use this one:

List<List<T>> newList = new ArrayList<>(size);
newList.add(item);
return newList;
like image 38
alex.b Avatar answered Nov 15 '22 19:11

alex.b


Surely the below is sufficient

final List<List<Object>> listOfList = new ArrayList<>(
            listToSplit.stream()
                    .collect(Collectors.groupingBy(el -> listToSplit.indexOf(el) / MAX_ROW_LENGTH))
                    .values()
    );

Stream it, collect with a grouping: this gives a Map of Object -> List, pull the values of the map and pass directly into whatever constructor (map.values() gives a Collection not a List).

like image 32
tom01 Avatar answered Nov 15 '22 20:11

tom01


Perhaps you can use something like that

 BiFunction<List,Integer,List> splitter= (list2, count)->{
            //temporary list of lists
            List<List> listOfLists=new ArrayList<>();

            //helper implicit recursive function
            BiConsumer<Integer,BiConsumer> splitterHelper = (offset, func) -> {
                if(list2.size()> offset+count){
                    listOfLists.add(list2.subList(offset,offset+count));

                    //implicit self call
                    func.accept(offset+count,func);
                }
                else if(list2.size()>offset){
                    listOfLists.add(list2.subList(offset,list2.size()));

                    //implicit self call
                    func.accept(offset+count,func);
                }
            };

            //pass self reference
            splitterHelper.accept(0,splitterHelper);

            return listOfLists;
        };

Usage example

List<Integer> list=new ArrayList<Integer>(){{
            add(1);
            add(2);
            add(3);
            add(4);
            add(5);
            add(6);
            add(7);
            add(8);
            add(8);
        }};

        //calling splitter function
        List listOfLists = splitter.apply(list, 3 /*max sublist size*/);

        System.out.println(listOfLists);

And as a result we have

[[1, 2, 3], [4, 5, 6], [7, 8, 8]]
like image 35
Vladyslav Nikolaiev Avatar answered Nov 15 '22 19:11

Vladyslav Nikolaiev