Рубрики
Без рубрики

Пример плоской карты Java 8

В этой статье объясняется flatMap потока Java 8() и как его использовать.

В этой статье объясняется Java 8 Stream.flatMap и как его использовать.

Тема

  1. Что такое flatMap?
  2. Зачем выравнивать ручей?
  3. Пример плоской карты – Найдите набор книг.
  4. Пример плоской карты – Порядок и линейные элементы.
  5. Пример плоской карты – Разбивает строку на пробелы.
  6. Плоская карта и примитивный тип

1. Что такое flatMap()?

1.1 Ознакомьтесь с приведенной ниже структурой. Он состоит из 2-уровневого потока или 2d-массивов.

# Stream
# Stream>
# String[][]

[
  [1, 2],
  [3, 4],
  [5, 6]
]

В Java 8 мы можем использовать flatMap для преобразования потока вышеуказанных 2 уровней в один уровень потока или 2d-массива в 1d-массив.

# Stream
# String[]

[1, 2, 3, 4, 5, 6]

2. Зачем выравнивать ручей?

2.1 Сложно обрабатывать поток, содержащий более одного уровня, например Поток<Строка[]> или Поток<Список<Элемент строки>> или Поток<Поток<Строка>> . И мы сглаживаем поток 2 уровней в один уровень, например Stream<Строка> или Поток <Элемент строки> , чтобы мы могли легко зациклить поток и обработать его.

Просмотрите приведенный ниже пример до и после применения flatMap к потоку.

2.2 Ниже приведен 2d-массив, и мы можем использовать Arrays.stream или Stream.of для преобразования его в поток, и он создает поток String[] или Stream<Строка[]> .

  String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

  // array to a stream
  Stream stream1 = Arrays.stream(array);

  // same result
  Stream stream2 = Stream.of(array);

или вот так.

[
  [a, b],
  [c, d],
  [e, f]
]       

2.3 Вот требование, мы хотим отфильтровать a и распечатать все символы.

Сначала мы попробуем Поток#фильтр напрямую. Однако приведенная ниже программа ничего не напечатает, и это потому, что x внутри фильтра Stream# является Строкой[] , а не строкой ; условие всегда будет оставаться ложным, и поток ничего не соберет.

  String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

  // convert array to a stream
  Stream stream1 = Arrays.stream(array);

  List result = stream1
      .filter(x -> !x.equals("a"))      // x is a String[], not String!
      .collect(Collectors.toList());

  System.out.println(result.size());    // 0

  result.forEach(System.out::println);  // print nothing?

Хорошо, на этот раз мы изменим метод фильтра, чтобы иметь дело со строкой [] .

  String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

  // array to a stream
  Stream stream1 = Arrays.stream(array);

  // x is a String[]
  List result = stream1
          .filter(x -> {
              for(String s : x){      // really?
                  if(s.equals("a")){
                      return false;
                  }
              }
              return true;
          }).collect(Collectors.toList());

  // print array
  result.forEach(x -> System.out.println(Arrays.toString(x)));

Выход

[c, d]
[e, f]

В приведенном выше случае Поток#фильтр отфильтрует весь [a, b] , но мы хотим отфильтровать только символ a

3.4 Ниже приведена окончательная версия, и мы сначала объединяем массив, а затем применяем фильтр. В Java для преобразования 2d-массива в 1d-массив мы можем зациклить 2d-массив и поместить все элементы в новый массив; Или мы можем использовать Java 8 flatMap для выравнивания 2d-массива в 1d-массив или из Потока<Строка[]> в Поток<Строка> .

  String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

  // Java 8
  String[] result = Stream.of(array)  // Stream
          .flatMap(Stream::of)        // Stream
          .toArray(String[]::new);    // [a, b, c, d, e, f]

  for (String s : result) {
      System.out.println(s);
  }

Выход

a
b
c
d
e
f

Теперь мы можем легко отфильтровать a ; давайте посмотрим окончательную версию.

  String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

  List collect = Stream.of(array)     // Stream
          .flatMap(Stream::of)                // Stream
          .filter(x -> !"a".equals(x))        // filter out the a
          .collect(Collectors.toList());      // return a List

  collect.forEach(System.out::println);

Выход

b
c
d
e
f

Я хочу отметить, что работа с более чем одним уровнем потока является сложной, запутанной и подверженной ошибкам, и мы можем использовать этот Поток#flatMap , чтобы сгладить поток 2 уровней в поток одного уровня.

Stream      -> flatMap ->	Stream
Stream>   -> flatMap ->	Stream
Stream>  -> flatMap ->	Stream
Stream>  -> flatMap ->	Stream

3. Пример плоской карты – Найти все книги.

В этом примере используется .stream() для преобразования Списка в поток объектов, и каждый объект содержит набор книг, и мы можем использовать flatMap для создания потока, содержащего все книги во всех объектах.

В конце концов, мы также отфильтровываем книгу, содержащую слово python , и собираем Набор , чтобы удалить дублированную книгу.

package com.mkyong.java8.stream.flatmap;

import java.util.HashSet;
import java.util.Set;

public class Developer {

    private Integer id;
    private String name;
    private Set book;

    //getters, setters, toString

    public void addBook(String book) {
        if (this.book == null) {
            this.book = new HashSet<>();
        }
        this.book.add(book);
    }
}
package com.mkyong.java8.stream.flatmap;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class FlatMapExample1 {

    public static void main(String[] args) {

        Developer o1 = new Developer();
        o1.setName("mkyong");
        o1.addBook("Java 8 in Action");
        o1.addBook("Spring Boot in Action");
        o1.addBook("Effective Java (3nd Edition)");

        Developer o2 = new Developer();
        o2.setName("zilap");
        o2.addBook("Learning Python, 5th Edition");
        o2.addBook("Effective Java (3nd Edition)");

        List list = new ArrayList<>();
        list.add(o1);
        list.add(o2);

        // hmm....Set of Set...how to process?
        /*Set> collect = list.stream()
                .map(x -> x.getBook())
                .collect(Collectors.toSet());*/

        Set collect =
                list.stream()
                        .map(x -> x.getBook())                              //  Stream>
                        .flatMap(x -> x.stream())                           //  Stream
                        .filter(x -> !x.toLowerCase().contains("python"))   //  filter python book
                        .collect(Collectors.toSet());                       //  remove duplicated

        collect.forEach(System.out::println);

    }

}

Выход

Spring Boot in Action
Effective Java (3nd Edition)
Java 8 in Action

карта необязательна.

    Set collect2 = list.stream()
                    //.map(x -> x.getBook())
                    .flatMap(x -> x.getBook().stream())                 //  Stream
                    .filter(x -> !x.toLowerCase().contains("python"))   //  filter python book
                    .collect(Collectors.toSet());

4. Пример плоской карты – Порядок и линейные элементы

Этот пример похож на официальный flatMap JavaDoc пример.

заказы – это поток заказов на покупку, и каждый заказ на покупку содержит набор позиций, затем мы можем использовать flatMap для создания потока или Потока <Позиция> , содержащего все позиции во всех заказах. Кроме того, мы также добавляем операцию уменьшить для суммирования общей суммы позиций.

package com.mkyong.java8.stream.flatmap;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

public class FlatMapExample2 {

    public static void main(String[] args) {

        List orders = findAll();

        /*
            Stream> listStream = orders.stream()
                    .map(order -> order.getLineItems());

            Stream lineItemStream = orders.stream()
                    .flatMap(order -> order.getLineItems().stream());
        */

        // sum the line items' total amount
        BigDecimal sumOfLineItems = orders.stream()
                .flatMap(order -> order.getLineItems().stream())    //  Stream
                .map(line -> line.getTotal())                       //  Stream
                .reduce(BigDecimal.ZERO, BigDecimal::add);          //  reduce to sum all

        // sum the order's total amount
        BigDecimal sumOfOrder = orders.stream()
                .map(order -> order.getTotal())                     //  Stream
                .reduce(BigDecimal.ZERO, BigDecimal::add);          //  reduce to sum all

        System.out.println(sumOfLineItems);                         // 3194.20
        System.out.println(sumOfOrder);                             // 3194.20

        if (!sumOfOrder.equals(sumOfLineItems)) {
            System.out.println("The sumOfOrder is not equals to sumOfLineItems!");
        }

    }

    // create dummy records
    private static List findAll() {

        LineItem item1 = new LineItem(1, "apple", 1, new BigDecimal("1.20"), new BigDecimal("1.20"));
        LineItem item2 = new LineItem(2, "orange", 2, new BigDecimal(".50"), new BigDecimal("1.00"));
        Order order1 = new Order(1, "A0000001", Arrays.asList(item1, item2), new BigDecimal("2.20"));

        LineItem item3 = new LineItem(3, "monitor BenQ", 5, new BigDecimal("99.00"), new BigDecimal("495.00"));
        LineItem item4 = new LineItem(4, "monitor LG", 10, new BigDecimal("120.00"), new BigDecimal("1200.00"));
        Order order2 = new Order(2, "A0000002", Arrays.asList(item3, item4), new BigDecimal("1695.00"));

        LineItem item5 = new LineItem(5, "One Plus 8T", 3, new BigDecimal("499.00"), new BigDecimal("1497.00"));
        Order order3 = new Order(3, "A0000003", Arrays.asList(item5), new BigDecimal("1497.00"));

        return Arrays.asList(order1, order2, order3);

    }
}
public class Order {

    private Integer id;
    private String invoice;
    private List lineItems;
    private BigDecimal total;

    // getter, setters, constructor
}
public class LineItem {

    private Integer id;
    private String item;
    private Integer qty;
    private BigDecimal price;
    private BigDecimal total;

    // getter, setters, constructor
}

Выход

3194.20
3194.20

5. Пример плоской карты – Разбивает строку на пробелы

В этом примере считывается текстовый файл, строка разделяется пробелами и отображается общее количество слов.

Текстовый файл.

hello world Java
hello world Python
hello world Node JS
hello world Rust
hello world Flutter

Прочитайте комментарий для пояснения.

package com.mkyong.java8.stream.flatmap;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class FlatMapExample3 {

    public static void main(String[] args) throws IOException {

        Path path = Paths.get("C:\\test\\test.txt");

        // read file into a stream of lines
        Stream lines = Files.lines(path, StandardCharsets.UTF_8);

        // stream of array...hard to process.
        // Stream words = lines.map(line -> line.split(" +"));

        // stream of stream of string....hmm...better flat to one level.
        // Stream> words = lines.map(line -> Stream.of(line.split(" +")));

        // result a stream of words, good!
        Stream words = lines.flatMap(line -> Stream.of(line.split(" +")));

        // count the number of words.
        long noOfWords = words.count();

        System.out.println(noOfWords);  // 16
    }
}

Выход

16

6. Плоская карта и примитивный тип

Для примитивных типов, таких как int , long , double и т.д. Поток Java 8 также предоставляет связанный flatMapTo{примитивный тип} для выравнивания потока примитивного типа; концепция та же.

Плоская карта -> Внутренний поток

package com.mkyong.java8.stream.flatmap;

import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class FlatMapExample4 {

    public static void main(String[] args) {

        int[] array = {1, 2, 3, 4, 5, 6};

        //Stream
        Stream streamArray = Stream.of(array);

        //Stream -> flatMap -> IntStream
        IntStream intStream = streamArray.flatMapToInt(x -> Arrays.stream(x));

        intStream.forEach(x -> System.out.println(x));

    }
}

Выход

1
2
3
4
5
6

flatMapТоЛонг -> Длинный поток

  long[] array = {1, 2, 3, 4, 5, 6};

  Stream longArray = Stream.of(array);

  LongStream longStream = longArray.flatMapToLong(x -> Arrays.stream(x));

  System.out.println(longStream.count());

Скачать Исходный Код

$клон git $клон git

$компакт-диск java8

Рекомендации

Оригинал: “https://mkyong.com/java8/java-8-flatmap-example/”