1 Как потоки упрощают код
Если есть требование, необходимо выполнить процесс для блюд, запрошенных из базы данных:
- Отфильтруйте блюда с менее чем 400 калориями
- Сортировать отфильтрованные блюда
- Получить названия отсортированных блюд
Блюдо: Dish.java
public class Dish {
private String name;
private boolean vegetarian;
private int calories;
private Type type;
// getter and setter
}
Реализация до Java 8
private List<String> beforeJava7(List<Dish> dishList) {
List<Dish> lowCaloricDishes = new ArrayList<>();
//1.筛选出卡路里小于400的菜肴
for (Dish dish : dishList) {
if (dish.getCalories() < 400) {
lowCaloricDishes.add(dish);
}
}
//2.对筛选出的菜肴进行排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
@Override
public int compare(Dish o1, Dish o2) {
return Integer.compare(o1.getCalories(), o2.getCalories());
}
});
//3.获取排序后菜肴的名字
List<String> lowCaloricDishesName = new ArrayList<>();
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
Реализация после Java 8
private List<String> afterJava8(List<Dish> dishList) {
return dishList.stream()
.filter(d -> d.getCalories() < 400) //筛选出卡路里小于400的菜肴
.sorted(comparing(Dish::getCalories)) //根据卡路里进行排序
.map(Dish::getName) //提取菜肴名称
.collect(Collectors.toList()); //转换为List
}
Не будьте небрежными, просто сделайте это за один раз.Функция, которая изначально требовала для реализации 24 кода, теперь может быть выполнена всего за 5 строк.
Рад закончить написание требований. На данный момент есть новые требования. Новые требования заключаются в следующем:
Классифицировать блюда, запрашиваемые из базы данных, в соответствии с типом блюд и возвращать результат Map
.
Если это размещено перед JDK8, кожу головы определенно будет онеметь
Реализация до Java 8
private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) {
Map<Type, List<Dish>> result = new HashMap<>();
for (Dish dish : dishList) {
//不存在则初始化
if (result.get(dish.getType())==null) {
List<Dish> dishes = new ArrayList<>();
dishes.add(dish);
result.put(dish.getType(), dishes);
} else {
//存在则追加
result.get(dish.getType()).add(dish);
}
}
return result;
}
К счастью, в jdk8 есть Stream, поэтому вам больше не нужно беспокоиться о сложных требованиях к обработке коллекций.
Реализация после Java 8
private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) {
return dishList.stream().collect(groupingBy(Dish::getType));
}
Еще одна строка кода решает проблему, я не могу не кричать о пакете коров Stream API. Увидев мощные функции потока, я подробно расскажу о потоке дальше.
2 Что такое поток
Поток — это последовательность элементов, сгенерированных из источника, который поддерживает операции обработки данных, которые могут быть массивами, файлами, коллекциями, функциями. Поток не является элементом коллекции, не является структурой данных и не хранит данные, его основная цель — вычисление
3 Как создать поток
Существует пять основных способов генерации потоков
1. Генерируется коллекцией, наиболее часто используемой в приложениях
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();
Генерация потока с помощью потокового метода коллекции
2. Генерируется массивом
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
Поток генерируется методом Arrays.stream, а поток, генерируемый этим методом, является числовым потоком [то есть IntStream] вместо Stream. Добавьте, что использование числовых потоков позволяет избежать распаковки во время вычислений и повысить производительность.
Stream API предоставляет три способа mapToInt, mapToDouble, mapToLong для преобразования потока объектов [т.е. Stream] в соответствующий числовой поток, а также предоставляет упакованный метод для преобразования числового потока в поток объектов.
3. Генерировать по значению
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Поток генерируется методом of Stream, а пустой поток может быть сгенерирован пустым методом Stream.
4. Генерируется файлом
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
Поток получается методом Files.line, и каждый полученный поток является строкой в заданном файле
5. Генерируется функцией Предоставляет два статических метода iterate и generate для генерации потоков из функций.
iterator
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
Метод iterate принимает два параметра, первый — значение инициализации, второй — операцию функции, которую нужно выполнить, потому что поток, сгенерированный итератором, является бесконечным потоком, поток усекается по методу limit, и только 5 четных чисел генерируются.
generator
Stream<Double> stream = Stream.generate(Math::random).limit(5);
Метод generate принимает один параметр, тип параметра метода — Supplier, который предоставляет значение для потока. Поток, сгенерированный generate, также является бесконечным потоком, поэтому поток усекается по limit
4 типа операций потоков
Существует два основных типа потоковых операций.
1. Промежуточная операция
За потоком может следовать ноль или более промежуточных операций. Его цель в основном состоит в том, чтобы открыть поток, выполнить некоторую степень отображения/фильтрации данных, а затем вернуть новый поток для использования следующей операцией. Этот тип операции является ленивым, и только вызов этого типа метода на самом деле не запускает обход потока.Настоящий обход должен дождаться терминальной операции.Обычные промежуточные операции включают фильтр, карту и т. д., которые будут представлены ниже.
2. Работа терминала
Поток имеет и может иметь только одну терминальную операцию.При выполнении этой операции поток закрывается и больше не может управляться.Поэтому поток можно пройти только один раз.Если вы хотите пройти, вам нужно сгенерировать поток через исходные данные. Выполнение терминальной операции действительно запустит обход потока. Такие как подсчет, сбор и т. д., которые будут представлены ниже.
5 Использование потока
Использование потоков будет разделено на терминальные операции и промежуточные операции.
Промежуточная операция
фильтрфильтр
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
При использовании метода фильтра для условной фильтрации параметр метода фильтра является условием.
отличительный удаляет повторяющиеся элементы
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();
Быстро удаляйте повторяющиеся элементы отдельным методом
limit возвращает указанное количество потоков
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);
Укажите количество возвращаемых потоков через метод limit.Значение параметра limit должно быть >=0, иначе будет выброшено исключение
skip пропускает элементы в потоке
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);
Пропустите элементы в потоке с помощью метода skip. В приведенном выше примере пропускаются первые два элемента, поэтому результат печати равен 2, 3, 4, 5. Значение параметра skip должно быть >= 0, иначе будет выдано исключение.
карта потока карта
Так называемое потоковое сопоставление заключается в сопоставлении принятого элемента с другим элементом.
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);
Сопоставление может быть выполнено с помощью метода map.В этом примере завершается сопоставление String -> Integer.В предыдущем примере завершено сопоставление Dish->String с помощью метода map.
Преобразование потока flatMap
Преобразование каждого значения в одном потоке в другой
List<String> wordList = Arrays.asList("Hello", "World");
List<String> strList = wordList.stream()
.map(w -> w.split(" "))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
Возвращаемое значение map(w -> w.split(" ")) равно Stream
совпадение элемента
Предусмотрены три метода сопоставления
1.allMatch соответствует всем
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println("值都大于3");
}
Реализуется методом allMatch
2.anyMatch соответствует одному из них
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println("存在大于3的值");
}
Эквивалентно
for (Integer i : integerList) {
if (i > 3) {
System.out.println("存在大于3的值");
break;
}
}
Если есть значение больше 3, выведите его.В java8 эта функция реализуется методом anyMatch.
3. noneMatch не соответствует всем
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println("值都小于3");
}
Реализуется методом noneMatch
6 Работа с терминалом
подсчитать количество элементов в потоке
1. По количеству
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();
Подсчитайте количество элементов в потоке с помощью метода count
2. По счету
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().collect(counting());
Последний метод подсчета элементов особенно полезен при использовании в сочетании с сбором.
найти
Предусмотрено два метода поиска
1.findFirst найти первый
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
Найдите первый элемент больше трех с помощью метода findFirst и напечатайте
2.findAny находит случайным образом
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();
Найдите один из элементов больше трех с помощью метода findAny и распечатайте его.Из-за внутренней оптимизации он заканчивается, когда будет найден первый элемент больше трех.Результат этого метода такой же, как и результат метода findFirst. Метод findAny предназначен для лучшего использования параллельных потоков, а метод findFirst более ограничен параллелизмом.
сокращение объединяет элементы в потоке
Предположим, мы суммируем значения в наборе
До jdk8
int sum = 0;
for (int i : integerList) {
sum += i;
}
После обработки jdk8 путем уменьшения
int sum = integerList.stream().reduce(0, (a, b) -> (a + b));
Это можно сделать одной строкой или сократить, используя ссылку на метод:
int sum = integerList.stream().reduce(0, Integer::sum);
reduce принимает два параметра, начальное значение здесь равно 0, и накопитель BinaryOperator для объединения двух элементов для получения нового значения.
Кроме того, метод сокращения имеет перегруженный метод без значения инициализации.
Получить минимальное и максимальное значения в потоке
Получить мин и макс по мин/макс
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);
Это также может быть записано как:
OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();
min получает минимальное значение в потоке, max получает максимальное значение в потоке, а параметр метода Comparator comparator
Получить минимум и максимум по minBy/maxBy
Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));
minBy получает минимальное значение в потоке, maxBy получает максимальное значение в потоке, а параметр метода Comparator comparator
Получите минимум и максимум, уменьшив
Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);
7 Резюме
Используя Stream API, можно упростить код, одновременно улучшить его читабельность и быстро использовать в проекте.