English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной учебник Java

Java Управление потоком

Java Массивы

Java Ориентированность на объекты (I)

Java Ориентированность на объекты (II)

Java Ориентированность на объекты (III)

Обработка исключений Java

Java Списки (List)

Java Queue (очередь)

Java Map-сборники

Java Set-сборники

Java Ввод/вывод (I/O)

Java Reader/Writer

Другие темы Java

Java 8 Stream

Java 8 новые возможности

Java 8 API добавила новую абстракцию под названием Stream, которая позволяет обрабатывать данные в декларативном стиле.

Stream предоставляет высокоуровневое абстрагирование для операций и выражений Java-коллекций, используя интуитивный способ, аналогичный SQL-запросам для извлечения данных из базы данных.

Stream API может значительно повысить продуктивность Java-программистов, позволяя им писать эффективный, чистый и лаконичный код.

Этот стиль рассматривает набор элементов, которые необходимо обработать, как поток, который передается по трубам и может обрабатываться на узлах труб, например, фильтрация, сортировка, агрегация и т.д.

Элементный поток проходит через промежуточную обработку (intermediate operation) в трубах и в конечном итоге получает результат предыдущей обработки от конечной операции (terminal operation).

+--------------------+ +------+ +------+ +---+ +-------+
| поток элементов +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+

Данный процесс можно преобразовать в Java-код следующим образом:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

Что такое Stream?

Stream (поток) — это очередь элементов из источника данных и поддерживает агрегационные операции

  • Элементная очередь Элементы — это объекты определенного типа, формирующие очередь. В Java потоки не хранят элементы, а вычисляют их по мере необходимости.

  • Источник данных Источник данных. Это может быть集合, массив, канал ввода-вывода, генератор и т.д.

  • Агрегационные операции Операции, аналогичные операциям SQL, например filter, map, reduce, find, match, sorted и т.д.

И в отличие от прежних операций Collection, Stream операции имеют две основные особенности:

  • Трубка: Все промежуточные операции возвращают сам объект потока. Таким образом, несколько операций можно串联ить в одну трубку, как в стиле потоковой обработки (fluent style). Это позволяет оптимизировать операции, например, отложенное выполнение (laziness) и короткое замыкание (short-circuiting).

  • Внутренняя итерация: Ранее итерация集合 производилась через Iterator или For-Each, явно вне коллекции, что называется внешней итерацией. Stream предоставляет внутреннюю итерацию, реализованную через паттерн посетителя (Visitor).

Создание потока

В Java 8 интерфейс集合 имеет два метода для создания потоков:

  • stream() − Создание последовательного потока для集合.

  • parallelStream()  − Создание параллельного потока для集合.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

forEach

Stream предоставляет новый метод 'forEach' для итерации каждого элемента потока. Следующий фрагмент кода использует forEach для вывода 10 случайных чисел:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

Метод map используется для отображения каждого элемента на соответствующий результат. В следующем фрагменте кода используется map для вывода квадратов элементов:

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// Получить соответствующие квадраты
List<Integer> squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());

filter

Метод filter используется для фильтрации элементов по заданным условиям. В следующем фрагменте кода используется метод filter для фильтрации пустых строк:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// Получить количество пустых строк
long count = strings.stream().filter(string -> string.isEmpty()).count();

limit

Метод limit используется для получения заданного количества потоков. В следующем фрагменте кода используется метод limit для печати 10 данных:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

Метод sorted используется для сортировки потоков. В следующем фрагменте кода используется метод sorted для сортировки 10 случайных чисел:

Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

Параллельный (parallel) программа

parallelStream является заменой для параллельного обработки потоков. В следующем примере мы используем parallelStream для вывода количества пустых строк:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// Получить количество пустых строк
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();

Мы можем легко переключаться между последовательным выполнением и параллельным.

Collectors

Класс Collectors реализует множество операций сокращения, таких как преобразование потока в набор и агрегирование элементов. Collectors можно использовать для возврата списка или строки:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("Отфильтрованный список: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Сожженные строки: " + mergedString);

Статистика

Кроме того, некоторые коллекторы, производящие результаты статистики, также очень полезны. Они主要用于 int, double, long и других базовых типах, и они могут использоваться для генерации результатов статистики,类似 таких:

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
 
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
 
System.out.println("Максимальное значение в списке: " + stats.getMax());
System.out.println("Минимальное значение в списке: " + stats.getMin());
System.out.println("Сумма всех чисел: " + stats.getSum());
System.out.println("Среднее значение: " + stats.getAverage());

Полный пример Stream

Поместите следующий код в файл Java8Tester.java:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
 
public class Java8Tester {
   public static void main(String args[]){
      System.out.println("Используя Java 7:");
        
      // Рассчитываем пустые строки
      List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
      System.out.println("Список: " + strings);
      long count = getCountEmptyStringUsingJava7(strings);
        
      System.out.println("Количество пустых символов: " + count);
      count = getCountLength2UsingJava7(strings);
        
      System.out.println("Количество строк длиной 3: " + count);
        
      // Удаление пустых строк
      List<String> filtered = deleteEmptyStringsUsingJava7(strings);
      System.out.println("Отфильтрованный список: " + filtered);
        
      // Удаление пустых строк и объединение их с помощью запятой
      String mergedString = getMergedStringUsingJava7(strings, ", ");
      System.out.println("Сожженные строки: " + mergedString);
      List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
        
      // Получение квадратов элементов списка
      List<Integer> squaresList = getSquares(numbers);
      System.out.println("Список квадратов: " + squaresList);
      List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
        
      System.out.println("Список: " + integers);
      System.out.println("Максимальное значение в списке: " + getMax(integers));
      System.out.println("Минимальное значение в списке: " + getMin(integers));
      System.out.println("Общая сумма: " + getSum(integers));
      System.out.println("Среднее значение: " + getAverage(integers));
      System.out.println("Случайные числа: ");
        
      // Вывод 10 случайных чисел
      Random random = new Random();
        
      for(int i=0; i < 10; i++){
         System.out.println(random.nextInt());
      }
        
      System.out.println("Используется Java 8:");
      System.out.println("Список: " + strings);
        
      count = strings.stream().filter(string->string.isEmpty()).count();
      System.out.println("Количество пустых строк: " + count);
        
      count = strings.stream().filter(string -> string.length() == 3).count();
      System.out.println("Количество строк длиной 3: " + count);
        
      filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
      System.out.println("Отфильтрованный список: " + filtered);
        
      mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
      System.out.println("Сожженные строки: " + mergedString);
        
      squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());
      System.out.println("Список квадратов: " + squaresList);
      System.out.println("Список: " + integers);
        
      IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
        
      System.out.println("Максимальное значение в списке: " + stats.getMax());
      System.out.println("Минимальное значение в списке: " + stats.getMin());
      System.out.println("Сумма всех чисел: " + stats.getSum());
      System.out.println("Среднее значение: " + stats.getAverage());
      System.out.println("Случайные числа: ");
        
      random.ints().limit(10).sorted().forEach(System.out::println);
        
      // Параллельная обработка
      count = strings.parallelStream().filter(string -> string.isEmpty()).count();
      System.out.println("空字符串的数量为: " + count);
   }
    
   private static int getCountEmptyStringUsingJava7(List<String> strings){
      int count = 0;
        
      for(String string: strings){
        
         if(string.isEmpty()){
            count++;
         }
      }
      return count;
   }
    
   private static int getCountLength2UsingJava7(List<String> strings){
      int count = 0;
        
      for(String string: strings){
        
         if(string.length() == 3){
            count++;
         }
      }
      return count;
   }
    
   private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){
      List<String> filteredList = new ArrayList<String>();
        
      for(String string: strings){
        
         if(!string.isEmpty()){
             filteredList.add(string);
         }
      }
      return filteredList;
   }
    
   private static String getMergedStringUsingJava7(List<String> strings, String separator){
      StringBuilder stringBuilder = new StringBuilder();
        
      for(String string: strings){
        
         if(!string.isEmpty()){
            stringBuilder.append(string);
            stringBuilder.append(separator);
         }
      }
      String mergedString = stringBuilder.toString();
      return mergedString.substring(0, mergedString.length()-2);
   }
    
   private static List<Integer> getSquares(List<Integer> numbers){
      List<Integer> squaresList = new ArrayList<Integer>();
        
      for(Integer number: numbers){
         Integer square = new Integer(number.intValue() * number.intValue());
            
         if(!squaresList.contains(square)){
            squaresList.add(square);
         }
      }
      return squaresList;
   }
    
   private static int getMax(List<Integer> numbers){
      int max = numbers.get(0);
        
      for(int i = 1; i < numbers.size(); i++){
        
         Integer number = numbers.get(i);
            
         if(number.intValue() > max){
            max = number.intValue();
         }
      }
      return max;
   }
    
   private static int getMin(List<Integer> numbers){
      int min = numbers.get(0);
        
      for(int i = 1; i < numbers.size(); i++){
         Integer number = numbers.get(i);
        
         if(number.intValue() < min){
            min = number.intValue();
         }
      }
      return min;
   }
    
   private static int getSum(List numbers){
      int sum = (int)(numbers.get(0));
        
      for(int i = 1; i < numbers.size(); i++){
         sum += (int)numbers.get(i);
      }
      return sum;
   }
    
   private static int getAverage(List<Integer> numbers){
      return getSum(numbers) / numbers.size();
   }
}

Выполнение вышеуказанного скрипта, результат вывода:

$ javac Java8Tester.java 
$ java Java8Tester
Использование Java 7: 
Список: [abc, , bc, efg, abcd, , jkl]
Количество пустых символов: 2
Количество строк длиной 3: 3
Отфильтрованный список: [abc, bc, efg, abcd, jkl]
Слияние строк: abc, bc, efg, abcd, jkl
Список квадратов: [9, 4, 49, 25]
Список: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Максимальное число в списке: 19
Минимальное число в списке: 1
Общее количество чисел: 85
Среднее значение: 9
Случайное число: 
-393170844
-963842252
447036679
-1043163142
-881079698
221586850
-1101570113
576190039
-1045184578
1647841045
Использование Java 8: 
Список: [abc, , bc, efg, abcd, , jkl]
Количество пустых строк: 2
Количество строк длиной 3: 3
Отфильтрованный список: [abc, bc, efg, abcd, jkl]
Слияние строк: abc, bc, efg, abcd, jkl
Список квадратов: [9, 4, 49, 25]
Список: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Максимальное число в списке: 19
Минимальное число в списке: 1
Общее количество чисел: 85
Среднее значение: 9.444444444444445
Случайное число: 
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
Количество пустых строк: 2

Java 8 новые возможности