Як я можу перетворити список списків у список у Java 8?


532

Якщо у мене є a List<List<Object>>, як я можу перетворити це на a, List<Object>що містить усі об'єкти в одному порядку ітерації, використовуючи функції Java 8?

Відповіді:


950

Ви можете використовувати flatMapдля вирівнювання внутрішніх списків (після перетворення їх у потоки) в один потік, а потім збирати результат у список:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

11
@arshajii правда, але я чомусь віддаю перевагу лямбдаському виразу. Можливо, мені не подобається зовнішній вигляд :::)
Еран

25
Class::methodспочатку відчуваєш себе трохи дивно, але має перевагу те, що він оголошує, з якого об’єкта ви збираєтесь. Це те, що ви інакше втрачаєте в потоках.
ArneHugo

2
Тим не менш, ви можете мати контейнер списку, і в цьому випадку вам може знадобитися використовувати лямбда (l-> l.myList.stream ()).
Міох

2
Якщо вам потрібно зробити явний кастинг (наприклад, масив примітивів для Список), можливо, знадобляться і лямбда.
Майкл Фултон,

52

flatmap краще, але є й інші способи досягти цього

List<List<Object>> listOfList = ... // fill

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);

37

flatMapМетод на , Streamбезумовно , може придавити ці списки для вас, але він повинен створювати Streamоб'єкти для елемента, то Streamдля результату.

Вам не потрібні всі ці Streamоб’єкти. Ось простий, стислий код для виконання завдання.

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);

Так як Listце Iterable, цей код викликає в forEachметод (Java 8 функцій), який успадковується від Iterable.

Виконує задану дію для кожного елемента до тих Iterableпір, поки всі елементи не будуть оброблені або дія не кине виняток. Дії виконуються в порядку ітерації, якщо вказаний порядок.

І А List«S Iteratorповертає елементи в послідовному порядку.

Для цього Consumerцей код передається у посиланні на метод (функція Java 8) до методу попередньо Java 8, List.addAllщоб послідовно додавати внутрішні елементи списку.

Додає всі елементи вказаної колекції до кінця цього списку в порядку, коли вони повертаються вказаним ітератором колекції (необов'язкова операція).


3
Хороша альтернативна пропозиція, що дозволяє уникнути зайвих виділень. Було б цікаво побачити основні купівлі цього використання під час роботи з деякими більшими колекціями, щоб побачити, як вони порівнюють один одного.
Пер Лундберг

12

Ви можете використовувати flatCollect()зразок із колекцій Eclipse .

MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);

Якщо ви не можете змінити список із List:

List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);

Примітка. Я є учасником колекцій Eclipse.


21
навіщо використовувати залежність від третьої сторони, коли функціональність надається Java 8?
saw303

2
API Eclipse Collections знаходиться в самій колекції, тому код є стислим, є однією з головних причин у цьому випадку.
Нікіл Нанівадекар

11

Так само, як згадував @Saravana:

плоска карта краще, але є й інші способи досягти цього

 listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
 });

Підводячи підсумок, існує кілька способів досягти того, як це:

private <T> List<T> mergeOne(Stream<List<T>> listStream) {
    return listStream.flatMap(List::stream).collect(toList());
}

private <T> List<T> mergeTwo(Stream<List<T>> listStream) {
    List<T> result = new ArrayList<>();
    listStream.forEach(result::addAll);
    return result;
}

private <T> List<T> mergeThree(Stream<List<T>> listStream) {
    return listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
    });
}

private <T> List<T> mergeFour(Stream<List<T>> listStream) {
    return listStream.reduce((l1, l2) -> {
        List<T> l = new ArrayList<>(l1);
        l.addAll(l2);
        return l;
    }).orElse(new ArrayList<>());
}

private <T> List<T> mergeFive(Stream<List<T>> listStream) {
    return listStream.collect(ArrayList::new, List::addAll, List::addAll);
}

11

Я просто хочу пояснити ще один сценарій , як List<Documents>, цей список містить кілька списків інших документів , таких як List<Excel>, List<Word>, List<PowerPoint>. Отже структура є

class A {
  List<Documents> documentList;
}

class Documents {
  List<Excel> excels;
  List<Word> words;
  List<PowerPoint> ppt;
}

Тепер, якщо ви хочете повторити програму Excel лише з документів, зробіть щось на кшталт нижче.

Отже, код був би

 List<Documents> documentList = new A().getDocumentList();

 //check documentList as not null

 Optional<Excel> excelOptional = documentList.stream()
                         .map(doc -> doc.getExcel())
                         .flatMap(List::stream).findFirst();
 if(excelOptional.isPresent()){
   Excel exl = optionalExcel.get();
   // now get the value what you want.
 }

Я сподіваюся, що це може вирішити чиюсь проблему під час кодування ...


1

Для цього ми можемо використовувати плоску карту, зверніться до коду нижче:

 List<Integer> i1= Arrays.asList(1, 2, 3, 4);
 List<Integer> i2= Arrays.asList(5, 6, 7, 8);

 List<List<Integer>> ii= Arrays.asList(i1, i2);
 System.out.println("List<List<Integer>>"+ii);
 List<Integer> flat=ii.stream().flatMap(l-> l.stream()).collect(Collectors.toList());
 System.out.println("Flattened to List<Integer>"+flat);

1

Розширення відповіді Ерана, яка була найвищою відповіддю, якщо у вас є купа шарів списків, ви можете продовжувати їх налагодження.

Це також поставляється із зручним способом фільтрації, коли ви також спускаєтесь по шарах, якщо потрібно.

Так, наприклад:

List<List<List<List<List<List<Object>>>>>> multiLayeredList = ...

List<Object> objectList = multiLayeredList
    .stream()
    .flatmap(someList1 -> someList1
        .stream()
        .filter(...Optional...))
    .flatmap(someList2 -> someList2
        .stream()
        .filter(...Optional...))
    .flatmap(someList3 -> someList3
        .stream()
        .filter(...Optional...))
    ...
    .collect(Collectors.toList())

У SQL це буде аналогічно встановленню операторів SELECT в операторах SELECT.


1

Спосіб перетворення List<List>на List:

listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

Дивіться цей приклад:

public class Example {

    public static void main(String[] args) {
        List<List<String>> listOfLists = Collections.singletonList(Arrays.asList("a", "b", "v"));
        List<String> list = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

        System.out.println("listOfLists => " + listOfLists);
        System.out.println("list => " + list);
    }

}       

Він друкує:

listOfLists => [[a, b, c]]
list => [a, b, c]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.