предисловие
Если некоторые друзья ранее не использовали метод цепного программирования потока java8 для разработки, они хотели бы изучить его. Если некоторые друзья узнали только часть использования, они хотят узнать больше. Если некоторые друзья хотят посмотреть, есть ли хорошие примеры для практической работы. Тогда поздравляю, эта статья идеально подходит для вас.
Во-первых, давайте посмотрим на отношения наследования потоков:
Родительскими интерфейсами Stream, IntStream, LongStream и DoubleStream являются BaseStream. Четыре метода подинтерфейса BaseStream аналогичны, за исключением того, что IntStream, LongStream и DoubleStream напрямую хранят базовые типы, что позволяет избежать автоматической упаковки/распаковки, и эффективность будет выше. Однако на самом деле мы немного больше используем Stream.
Давайте еще раз посмотрим на рабочий процесс потока:
Зачем изучать метод цепного программирования потока
бизнес-требование 1: Задайте массив строк, найдите в нем одинаковые элементы и подсчитайте количество повторений.
Раньше мы делали что-то вроде этого:
public class CountTest {
@Test
public void testCount1() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
Map<String, Long> countMap = new HashMap<>();
for (String data : list) {
Long aLong = countMap.get(data);
if (Objects.isNull(aLong)) {
countMap.put(data, 1L);
} else {
countMap.put(data, ++aLong);
}
}
countMap.forEach((key, value) -> System.out.println("key:" + key + " value:" + value));
}
}
Результаты:
key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Давайте посмотрим, что мы можем сделать, если воспользуемся потоком java8:
public class CountTest {
@Test
public void testCount2() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
countMap.forEach((key, value) -> System.out.println("key:" + key + " value:" + value));
}
}
Результаты:
key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Мы видим, что testCount1 и testCount2 имеют одинаковый результат выполнения, всего одну строку кода:
Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Логика нескольких строк кода в приведенном выше testCount1 может быть реализована.
бизнес-требование 2: из указанного массива строк найти, существует ли указанная строка.
Раньше мы делали что-то вроде этого:
public class FindTest {
@Test
public void testFind1() {
String findStr = "bd";
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
boolean match = false;
for (String data : list) {
if (data.equals(findStr)) {
match = true;
break;
}
}
//结果:match:true
System.out.println("match:" + match);
}
}
Давайте посмотрим, что мы можем сделать, если воспользуемся потоком java8:
public class MatchTest {
@Test
public void testFind2() {
String findStr = "bd";
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
boolean match = list.stream().anyMatch(x -> x.equals(findStr));
//结果:match:true
System.out.println("match:" + match);
}
}
Мы видим, что результаты вызова методов testFind1 и testFind2 совпадают. Однако с синтаксисом потока java8 функция завершается всего одной строкой кода, и это здорово.
Супер подробное руководство по использованию java8 stream
Потоковые операторы условно делятся на два типа: промежуточные операторы и операторы завершения.
Промежуточная операция:
1.filter(T-> boolean)
Фильтровать данные, сохранять элементы, логическое значение которых равно true, возвращать коллекцию
public class FilterTest {
@Test
public void testFilter() {
List<Integer> list = Lists.newArrayList(20, 23, 25, 28, 30, 33, 37, 40);
//从指定数据集合中过滤出大于等于30的数据集合
List<Integer> collect = list.stream().filter(x -> x >= 30).collect(Collectors.toList());
//结果:[30, 33, 37, 40]
System.out.println(collect);
}
}
collect(Collectors.toList()) может преобразовывать поток в тип списка, сбор на самом деле является операцией завершения.
2.map(T -> R)
Оператор преобразования может выполнять преобразование данных, например: преобразование строки в int, long, double или преобразование одного объекта в другой. Содержит: map, mapToInt, mapToLong, mapToDouble
public class MapTest {
@Test
public void testMap() {
List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<Long> collect1 = list.stream().map(x -> Long.parseLong(x)).collect(Collectors.toList());
//结果:[1, 2, 3, 4, 5, 6]
System.out.println(collect1);
//结果:111111
list.stream().mapToInt(x -> x.length()).forEach(System.out::print);
System.out.println("");
//结果:111111
list.stream().mapToLong(x -> x.length()).forEach(System.out::print);
System.out.println("");
//结果:1.01.01.01.01.01.0
list.stream().mapToDouble(x -> x.length()).forEach(System.out::print);
}
}
3.flatMap(T -> Stream)
Сопоставьте каждый элемент T в потоке с потоком и подключите каждый поток к потоку.
public class FlatMapTest {
@Test
public void testFlatMap() {
List<List<String>> list = new ArrayList<List<String>>(){{
add(Lists.newArrayList("a","b","c"));
add(Lists.newArrayList("d","e","f"));
add(Lists.newArrayList("j","k","y"));
}};
//结果:[[a, b, c], [d, e, f], [j, k, y]]
System.out.println(list);
List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());
//结果:[a, b, c, d, e, f, j, k, y]
System.out.println(collect);
}
}
Мы видим, что flatMap может легко преобразовывать двумерные данные строк в однобитные массивы.
4.distinct
Дедупликация, аналогичная функции Different в msql, использует метод equals для сравнения внизу.
public class DistinctTest {
@Test
public void testDistinct() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
List<String> collect = list.stream().distinct().collect(Collectors.toList());
//结果:[a, b, ab, abc, abcd, bd]
System.out.println(collect);
}
}
На самом деле есть еще один способ дедупликации, вы можете использовать Collectors.toSet(), о котором мы поговорим позже.
5.sorted
Для сортировки элементов необходимо реализовать интерфейс Comparable, конечно, вы также можете настроить компаратор.
public class SortTest {
@Test
public void testSort() {
List<Integer> list = Lists.newArrayList(5, 3, 7, 1, 4, 6);
List<Integer> collect = list.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList());
//结果:[1, 3, 4, 5, 6, 7]
System.out.println(collect);
}
}
6.limit
Операция ограничения тока чем-то похожа на функцию ограничения в mysql, например: элементов 10, берутся только первые 3 элемента
public class LimitTest {
@Test
public void testLimit() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
List<String> collect = list.stream().limit(3).collect(Collectors.toList());
//结果:[a, b, ab]
System.out.println(collect);
}
}
7.skip
Пропустить операцию, например: есть 10 элементов, начать с 5-го элемента и перейти к следующим элементам
public class SkipTest {
@Test
public void testSkip() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
List<String> collect = list.stream().skip(5).collect(Collectors.toList());
//结果:[ab, a, abcd, bd, abc]
System.out.println(collect);
}
}
8.peek
выбрать операции,
public class PeekTest {
@Test
public void testPeek() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
//结果:abababcaabaabcdbdabc
list.stream().peek(x -> x.toUpperCase()).forEach(System.out::print);
}
}
Внимательные друзья обнаружат, что преобразование x.toUpperCase() в функцию верхнего регистра на самом деле не действует. Попробуйте изменить метод просмотра карты:
public class PeekTest {
@Test
public void testPeek() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc");
//结果:ABABABCAABAABCDBDABC
list.stream().map(x -> x.toUpperCase()).forEach(System.out::print);
}
}
Мы можем видеть, что преобразование в функцию верхнего регистра с помощью операции карты вступает в силу, но операция просмотра не вступает в силу. peek выполняет только некоторые операции над элементами в потоке, но данные после операции не возвращаются в поток, поэтому элементы в потоке по-прежнему являются исходными элементами.
Завершить операцию:
1.forEach
Операции обхода, в том числе: forEach и forEachOrdered
forEach: поддерживает параллельную обработку
forEachOrdered: обрабатывается по порядку, скорость обхода медленнее
public class ForEachTest {
@Test
public void testForEach() {
List<String> list = Lists.newArrayList("a", "b", "ab");
//结果:a b ab
list.stream().forEach(x-> System.out.print(x+' '));
System.out.println("");
//可以简化
//结果:a b ab
list.forEach(x-> System.out.print(x+' '));
System.out.println("");
//结果:a b ab
list.stream().forEachOrdered(x-> System.out.print(x+' '));
}
}
2.collect
Собери операцию, собери все элементы, Collectors предоставляет множество сборщиков. В том числе: toMap, toSet, toList, joining, groupingBy, maxBy, minBy и другие операции.
toMap: преобразовать поток данных в карту, элементы, содержащиеся в нем, представлены в виде ключ/значение
toSet: преобразовать поток данных в набор, содержащиеся в нем элементы не могут повторяться
toList: преобразовать поток данных в список, а содержащиеся в нем элементы упорядочены
присоединение: сращивание строк
groupingBy: группировка, вы можете преобразовать список в карту
couting: подсчитать количество элементов
maxBy: получить самый большой элемент
minBy: получить наименьший элемент
summarizingInt: суммирует элементы типа int, возвращает IntSummaryStatistics, а затем вызывает определенные методы для подсчета элементов: getCount (статистика), getSum (суммирование), getMin (получение минимального значения), getMax (получение максимального значения), getAverage (получение максимальное значение) среднее значение)
summarizingLong: Суммировать элементы типа long, использование такое же, как и для summarizingInt.
summarizingDouble: суммировать элементы типа double, использование такое же, как и summarizingInt.
averagingInt: получить среднее значение элементов типа int и вернуть данные типа double
averagingLong: получить среднее значение элементов типа long, то же использование, что и averagingInt.
averagingDouble: получить среднее значение элементов типа double, то же использование, что и averagingInt.
сопоставление: получите сопоставление, вы можете вернуть часть исходного элемента в качестве нового элемента
public class CollectTest {
@Data
@AllArgsConstructor
class User {
private String name;
private Integer age;
}
@Test
public void testCollect() {
List<String> list0 = Lists.newArrayList("a", "b", "ab");
Map<String, String> collect0 = list0.stream().collect(Collectors.toMap(String::new, Function.identity()));
//结果:{ab=ab, a=a, b=b}
System.out.println(collect0);
List<String> list = Lists.newArrayList("a", "b", "ab", "a", "b", "ab");
List<String> collect1 = list.stream().collect(Collectors.toList());
//结果:[a, b, ab, a, b, ab]
System.out.println(collect1);
//结果:[a, ab, b]
Set<String> collect2 = list.stream().collect(Collectors.toSet());
System.out.println(collect2);
String collect3 = list.stream().collect(Collectors.joining(","));
//结果:a,b,ab,a,b,ab
System.out.println(collect3);
Map<String, List<String>> collect4 = list.stream().collect(Collectors.groupingBy(Function.identity()));
//结果:{ab=[ab, ab], a=[a, a], b=[b, b]}
System.out.println(collect4);
Long collect = list.stream().collect(Collectors.counting());
//结果:6
System.out.println(collect);
String collect5 = list.stream().collect(Collectors.maxBy((a, b) -> a.compareTo(b))).orElse(null);
//结果:b
System.out.println(collect5);
String collect6 = list.stream().collect(Collectors.minBy((a, b) -> a.compareTo(b))).orElse(null);
//结果:a
System.out.println(collect6);
List<String> list2 = Lists.newArrayList("2", "3", "5");
IntSummaryStatistics summaryStatistics = list2.stream().collect(Collectors.summarizingInt(x -> Integer.parseInt(x)));
long sum = summaryStatistics.getSum();
//结果:10
System.out.println(sum);
Double collect7 = list2.stream().collect(Collectors.averagingInt(x -> Integer.parseInt(x)));
//结果:3.3333333333333335
System.out.println(collect7);
List<User> userList = new ArrayList<User>() {{
add(new User("jack",23));
add(new User("james",30));
add(new User("curry",28));
}};
List<String> collect8 = userList.stream().collect(Collectors.mapping(User::getName, Collectors.toList()));
//[jack, james, curry]
System.out.println(collect8);
}
}
3.find
Операции поиска, в том числе: findFirst, findAny
findFirst: найти первый, возвращаемый тип является необязательным
findAny: первый элемент найден при использовании stream(), а один из элементов найден при параллельном использовании parallelStream(), а возвращаемый тип является необязательным.
public class FindOpTest {
@Test
public void testFindOp() {
List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "bc", "ab");
//查找第一匹配的元素
String data1 = list.stream().findFirst().orElse(null);
//结果: a
System.out.println(data1);
String data2 = list.stream().findAny().orElse(null);
//结果: a
System.out.println(data2);
}
}
4.match
Операции сопоставления, в том числе: allMatch, anyMatch, noneMatch
allMatch: все элементы соответствуют условию, возвращают логический тип
anyMatch: любой элемент удовлетворяет условию, возвращает логический тип
noneMatch: все элементы не соответствуют условию, возвращают логический тип
public class MatchTest {
@Test
public void testMatch() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
boolean allMatch = list.stream().allMatch(x -> x > 1);
//结果:true
System.out.println(allMatch);
boolean allMatch2 = list.stream().allMatch(x -> x > 2);
//结果:false
System.out.println(allMatch2);
boolean anyMatch = list.stream().anyMatch(x -> x > 2);
//结果:true
System.out.println(anyMatch);
boolean noneMatch1 = list.stream().noneMatch(x -> x > 5);
//结果:false
System.out.println(noneMatch1);
boolean noneMatch2 = list.stream().noneMatch(x -> x > 7);
//结果:true
System.out.println(noneMatch2);
}
}
5.count
Статистическая операция, эффект аналогичен вызову метода size() коллекции
public class CountOpTest {
@Test
public void testCountOp() {
List<String> list = Lists.newArrayList("a", "b", "ab");
long count = list.stream().count();
//结果:3
System.out.println(count);
}
}
6.мин, макс
min: получить минимальное значение и вернуть данные необязательного типа.
max: получить максимальное значение и вернуть данные необязательного типа.
public class MaxMinTest {
@Test
public void testMaxMin() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
Optional<Integer> max = list.stream().max((a, b) -> a.compareTo(b));
//结果:7
System.out.println(max.get());
Optional<Integer> min = list.stream().min((a, b) -> a.compareTo(b));
//结果:2
System.out.println(min.get());
}
}
7.reduce
Операция сокращения уменьшает значение всего потока данных до значения Нижний слой count, min и max использует сокращение.
Операция сокращения может быть реализована для генерации значения из Stream, причем сгенерированное значение не произвольно, а в соответствии с заданной моделью расчета.
public class ReduceTest {
@Test
public void testReduce() {
List<Integer> list = Lists.newArrayList(2, 3, 5, 7);
Integer sum1 = list.stream().reduce(0, Integer::sum);
//结果:17
System.out.println(sum1);
Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
//结果:17
System.out.println(reduce.get());
Integer max = list.stream().reduce(0, Integer::max);
//结果:7
System.out.println(max);
Integer min = list.stream().reduce(0, Integer::min);
//结果:0
System.out.println(min);
Optional<Integer> reduce1 = list.stream().reduce((a, b) -> a > b ? b : a);
//2
System.out.println(reduce1.get());
}
}
8.toArray
Операции с массивами преобразуют элементы потока данных в массив.
public class ArrayTest {
@Test
public void testArray() {
List<String> list = Lists.newArrayList("a", "b", "ab");
String[] strings = list.stream().toArray(String[]::new);
//结果:a b ab
for (int i = 0; i < strings.length; i++) {
System.out.print(strings[i]+" ");
}
}
}
Разница между потоком и parallelStream поток: это единый конвейер, называемый потоком, который в основном используется для логической обработки коллекций.
parallelStream: Это мультиконвейер, который обеспечивает параллельную обработку потоков. Это еще одна важная особенность Stream. Нижний уровень реализован с использованием фреймворка Fork/Join.
public class StreamTest {
@Test
public void testStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:1234567
list.stream().forEach(System.out::print);
}
}
public class ParallelStreamTest {
@Test
public void testParallelStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:5726134
list.parallelStream().forEach(System.out::print);
}
}
Мы видим, что использование forEach для parallelStream напрямую для обхода данных не является последовательным.
Что делать, если вы хотите сделать обход parallelStream по порядку?
public class ParallelStreamTest {
@Test
public void testParallelStream() {
List<Integer> list = Lists.newArrayList(1,2, 3,4, 5,6, 7);
//结果:1234567
list.parallelStream().forEachOrdered(System.out::print);
}
}
Как работает parallelStream:
Кейсы на работе
1. Найдите одинаковый элемент из двух наборов. Обычно он используется в сценарии пакетного импорта данных, сначала запрашивая данные, а затем добавляя или изменяя их в пакетах.
public class WorkTest {
@Test
public void testWork1() {
List<String> list1 = Lists.newArrayList("a", "b", "ab");
List<String> list2 = Lists.newArrayList("a", "c", "ab");
List<String> collect = list1.stream()
.filter(x -> list2.stream().anyMatch(e -> e.equals(x)))
.collect(Collectors.toList());
//结果:[a, ab]
System.out.println(collect);
}
}
2. Есть два множества a и b, отфильтровать элементы из множества a, но не из множества b. Эту ситуацию можно использовать, если указан набор идентификаторов, набор данных запрашивается из базы данных в соответствии с набором идентификаторов, а затем идентификаторы, которые не существуют в наборе данных, фильтруются в соответствии с набором идентификаторов, и эти идентификаторы нуждаются быть добавленным.
@Test
public void testWork2() {
List<String> list1 = Lists.newArrayList("a", "b", "ab");
List<String> list2 = Lists.newArrayList("a", "c", "ab");
List<String> collect = list1.stream()
.filter(x -> list2.stream().noneMatch(e -> e.equals(x)))
.collect(Collectors.toList());
//结果:[b]
System.out.println(collect);
}
3. Фильтруйте данные в соответствии с условиями и повторяйте преобразование данных.
@AllArgsConstructor
@Data
class User {
private String name;
private Integer age;
}
@Test
public void testWork3() {
List<User> userList = new ArrayList<User>() {{
add(new User("jack",23));
add(new User("james",30));
add(new User("curry",28));
add(new User("tom",27));
add(new User("sue",29));
}};
List<String> collect = userList.stream()
.filter(x -> x.getAge() > 27)
.sorted((a, b) -> a.getAge().compareTo(b.getAge()))
.limit(2)
.map(User::getName)
.collect(Collectors.toList());
//结果:[curry, sue]
System.out.println(collect);
}
4. Подсчитайте самый молодой возраст среди людей с таким же именем в указанном наборе
@Test
public void testWork4() {
List<User> userList = new ArrayList<User>() {{
add(new User("tom", 23));
add(new User("james", 30));
add(new User("james", 28));
add(new User("tom", 27));
add(new User("sue", 29));
}};
userList.stream().collect(Collectors.groupingBy(User::getName))
.forEach((name, list) -> {
User user = list.stream().sorted((a, b) -> a.getAge().compareTo(b.getAge())).findFirst().orElse(null);
//结果:name:sue,age:29
// name:tom,age:23
// name:james,age:28
System.out.println("name:" + name + ",age:" + user.getAge());
});
}
Последнее слово (пожалуйста, обратите внимание, не проституируйте меня по пустякам)
Если эта статья полезна или вдохновляет вас, пожалуйста, помогите обратить внимание, ваша поддержка является самой большой мотивацией для меня, чтобы продолжать писать.
Попросите в один клик три ссылки: лайк, вперед и смотреть.
Кроме того, обратите внимание на официальный аккаунт: [Су Сан сказал о технологии] и ответьте в официальном аккаунте: интервью, артефакты кода, руководства по разработке, управление временем имеют отличные преимущества для поклонников, и ответьте: добавьте группу, вы можете общаться и общаться со многими старшими производителями BAT.