preface
If some friends have not used java8 Stream before this kind of chained programming way to do development, want to learn. If some of your friends have only learned part of the usage, they want to learn more. If some of you want to see if there’s a good example that works in real life. So congratulations, this article is perfect for you.
First, let’s look at the stream inheritance:
The parent interface of Stream, IntStream, LongStream, DoubleStream is BaseStream. BaseStream’s four subinterface methods are all the same, except that IntStream, LongStream, and DoubleStream store the basic types directly, avoiding automatic loading and unpacking and making it more efficient. However, we actually use Stream a little more.
Let’s look at the stream workflow diagram:
Why do you want to learn stream chain programming
Business requirement 1: Specify an array of strings, find the same elements, and count the number of repetitions.
We used to do something like this:
public class CountTest {
@Test
public void testCount1(a) {
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)); }}Copy the code
Execution Result:
key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Copy the code
Let’s see what we can do with a stream from java8:
public class CountTest {
@Test
public void testCount2(a) {
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)); }}Copy the code
Execution Result:
key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Copy the code
We can see that testCount1 and testCount2 execute the same result with only one line of code:
Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Copy the code
You can implement the multi-line logic in testCount1 above.
Business requirement 2: Find if the specified string exists from a specified string array
We used to do something like this:
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; }} // Result: match:true system.out.println ("match:" + match); }}Copy the code
Let’s see what we can do with a stream from 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)); Println ("match:" + match); // Result: match:true system.out.println ("match:" + match); }}Copy the code
We can see that calling testFind1 and testFind2 results in the same result. However, using Java8 Stream syntax, it’s nice to do it again in one line of code.
Java8 Stream super detailed usage guide
The stream operators are roughly divided into two types: intermediate operators and termination operators
Intermediate operation:
1.filter(T-> boolean)
Filter the data, retaining elements whose Boolean is true, and return a collection
public class FilterTest {
@Test
public void testFilter(a) {
List<Integer> list = Lists.newArrayList(20.23.25.28.30.33.37.40);
// Filter data sets greater than or equal to 30 from the specified data set
List<Integer> collect = list.stream().filter(x -> x >= 30).collect(Collectors.toList());
// Result: [30, 33, 37, 40]System.out.println(collect); }}Copy the code
Collect (Collectors. ToList ()) converts a stream to a List type. Collect is actually a termination operation.
2.map(T -> R)
Conversion operators that convert data, such as string to int, long, double, or one entity to another. Contains map, mapToInt, mapToLong, and mapToDouble
public class MapTest {
@Test
public void testMap(a) {
List<String> list = Lists.newArrayList("1"."2"."3"."4"."5"."6");
List<Long> collect1 = list.stream().map(x -> Long.parseLong(x)).collect(Collectors.toList());
// Result: [1, 2, 3, 4, 5, 6]
System.out.println(collect1);
// Result: 111111
list.stream().mapToInt(x -> x.length()).forEach(System.out::print);
System.out.println("");
// Result: 111111
list.stream().mapToLong(x -> x.length()).forEach(System.out::print);
System.out.println("");
// Result: 1.01.01.01.01.01.0list.stream().mapToDouble(x -> x.length()).forEach(System.out::print); }}Copy the code
3.flatMap(T -> Stream)
Map each element T in the flow to a flow, and concatenate each flow into a flow
public class FlatMapTest {
@Test
public void testFlatMap(a) {
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"));
}};
// Results: [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); }}Copy the code
We can see that flatMap makes it easy to turn two-dimensional data from strings into one-bit arrays.
4.distinct
Deduplication, similar to the use of DISTINCT in MSQL, uses the equals method at the bottom for comparison.
public class DistinctTest {
@Test
public void testDistinct(a) {
List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
List<String> collect = list.stream().distinct().collect(Collectors.toList());
// Result: [a, b, ab, ABC, abcd, bd]System.out.println(collect); }}Copy the code
There is another way to get rid of Collectors, and you can use it later. Collectors. ToSet ().
5.sorted
Order elements, provided you implement the Comparable interface, and of course you can customize comparators.
public class SortTest {
@Test
public void testSort(a) {
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());
// Result: [1, 3, 4, 5, 6, 7]System.out.println(collect); }}Copy the code
6.limit
If there are 10 elements, select only the first 3 elements
public class LimitTest {
@Test
public void testLimit(a) {
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());
// Result: [a, b, ab]System.out.println(collect); }}Copy the code
7.skip
Skip operations, such as: there are 10 elements, starting from the fifth element to the next element
public class SkipTest {
@Test
public void testSkip(a) {
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());
// Result: [ab, a, abcd, bd, ABC]System.out.println(collect); }}Copy the code
8.peek
Pick out operation,
public class PeekTest {
@Test
public void testPeek(a) {
List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
// Result: abababcaabaabcdbdabclist.stream().peek(x -> x.toUpperCase()).forEach(System.out::print); }}Copy the code
Sharp-eyed friends will notice that the x.topperCase () conversion to uppercase doesn’t actually work. Try changing peek to map:
public class PeekTest {
@Test
public void testPeek(a) {
List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
// Result: ABABABCAABAABCDBDABClist.stream().map(x -> x.toUpperCase()).forEach(System.out::print); }}Copy the code
As you can see, conversion to uppercase works with the map operation, but not with peek. Peek only performs operations on elements in the Stream, but the data is not returned to the Stream, so the elements in the Stream remain the same.
Termination operation:
1.forEach
Traversal operations, including forEach and forEachOrdered
ForEach: supports parallel processing
ForEachOrdered: The order is processed and the traversal speed is slow
public class ForEachTest {
@Test
public void testForEach(a) {
List<String> list = Lists.newArrayList("a"."b"."ab");
// Result: A, B, ab
list.stream().forEach(x-> System.out.print(x+' '));
System.out.println("");
// Can be simplified
// Result: A, B, ab
list.forEach(x-> System.out.print(x+' '));
System.out.println("");
// Result: A, B, ab
list.stream().forEachOrdered(x-> System.out.print(x+' ')); }}Copy the code
2.collect
Collectors, which collect all elements, provide many Collectors. Including toMap, toSet, toList, Joining, groupingBy, maxBy, and minBy.
ToMap: Transforms the data stream into a map that contains elements in the form of keys/values
ToSet: Converts a data stream to a set containing elements that cannot be repeated
ToList: Flow the data out as a list, which contains ordered elements
Joining: joining a string
GroupingBy: Groups a list into a map
Couting: Count elements
MaxBy: Gets the maximum element
MinBy: Gets the smallest element
summarizingInt: Summarize an element of type int, return IntSummaryStatistics, and call a specific method to count the elements: GetCount, getSum, getMin, getMax, getAverage
SummarizingLong: Element of type summarizingLong, same as summarizingInt
SummarizingDouble: A summarizingDouble element, same as summarizingInt
AveragingInt: Gets the average value of elements of type int and returns a double
AveragingLong: Gets the average value of elements of type long. Use the same as averagingInt
AveragingDouble: Gets the average value of elements of type double. Use the same way as averagingInt
Mapping: Gets a mapping that returns a portion of the original element as a new element
public class CollectTest {
@Data
@AllArgsConstructor
class User {
private String name;
private Integer age;
}
@Test
public void testCollect(a) {
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());
// Result: [a, b, ab, a, b, ab]
System.out.println(collect1);
// result: [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());
// Result: 6
System.out.println(collect);
String collect5 = list.stream().collect(Collectors.maxBy((a, b) -> a.compareTo(b))).orElse(null);
// result: b
System.out.println(collect5);
String collect6 = list.stream().collect(Collectors.minBy((a, b) -> a.compareTo(b))).orElse(null);
// result: 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();
// Result: 10
System.out.println(sum);
Double collect7 = list2.stream().collect(Collectors.averagingInt(x -> Integer.parseInt(x)));
// Result: 3.333333333335
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); }}Copy the code
3.find
Search operations, including findFirst and findAny
FindFirst: Finds the first one and returns type Optional
FindAny: The first element is found using stream(), and one of the elements is found using parallelStream(), and the type returned is Optional
public class FindOpTest {
@Test
public void testFindOp(a) {
List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."bc"."ab");
// Find the first matched element
String data1 = list.stream().findFirst().orElse(null);
// result: a
System.out.println(data1);
String data2 = list.stream().findAny().orElse(null);
// result: aSystem.out.println(data2); }}Copy the code
4.match
Matching operations, including allMatch, anyMatch, and noneMatch
AllMatch: Returns Boolean if all elements meet the criteria
AnyMatch: Returns Boolean if any element meets the condition
NoneMatch: All elements do not meet the criteria, return Boolean
public class MatchTest {
@Test
public void testMatch(a) {
List<Integer> list = Lists.newArrayList(2.3.5.7);
boolean allMatch = list.stream().allMatch(x -> x > 1);
// Result: true
System.out.println(allMatch);
boolean allMatch2 = list.stream().allMatch(x -> x > 2);
// Result: false
System.out.println(allMatch2);
boolean anyMatch = list.stream().anyMatch(x -> x > 2);
// Result: true
System.out.println(anyMatch);
boolean noneMatch1 = list.stream().noneMatch(x -> x > 5);
// Result: false
System.out.println(noneMatch1);
boolean noneMatch2 = list.stream().noneMatch(x -> x > 7);
// Result: trueSystem.out.println(noneMatch2); }}Copy the code
5.count
The effect is similar to that of calling the size() method of the collection
public class CountOpTest {
@Test
public void testCountOp(a) {
List<String> list = Lists.newArrayList("a"."b"."ab");
long count = list.stream().count();
// Result: 3System.out.println(count); }}Copy the code
6. Min and Max
Min: Gets the minimum value and returns data of type Optional
Max: Gets the maximum value. Returns data of type Optional
public class MaxMinTest {
@Test
public void testMaxMin(a) {
List<Integer> list = Lists.newArrayList(2.3.5.7);
Optional<Integer> max = list.stream().max((a, b) -> a.compareTo(b));
// Result: 7
System.out.println(max.get());
Optional<Integer> min = list.stream().min((a, b) -> a.compareTo(b));
// Result: 2System.out.println(min.get()); }}Copy the code
7.reduce
Protocol operation, the value of the entire data stream is reduced to a value, count, min, Max is the underlying use of reduce.
The reduce operation enables the generation of a value from a Stream, not arbitrarily, but according to a specified calculation model.
public class ReduceTest {
@Test
public void testReduce(a) {
List<Integer> list = Lists.newArrayList(2.3.5.7);
Integer sum1 = list.stream().reduce(0, Integer::sum);
// Result: 17
System.out.println(sum1);
Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
// Result: 17
System.out.println(reduce.get());
Integer max = list.stream().reduce(0, Integer::max);
// Result: 7
System.out.println(max);
Integer min = list.stream().reduce(0, Integer::min);
// Result: 0
System.out.println(min);
Optional<Integer> reduce1 = list.stream().reduce((a, b) -> a > b ? b : a);
/ / 2System.out.println(reduce1.get()); }}Copy the code
8.toArray
Array operations that convert elements of a data stream to arrays.
public class ArrayTest {
@Test
public void testArray(a) {
List<String> list = Lists.newArrayList("a"."b"."ab");
String[] strings = list.stream().toArray(String[]::new);
// Result: A, B, ab
for (int i = 0; i < strings.length; i++) {
System.out.print(strings[i]+""); }}}Copy the code
A stream is a single pipe, called a stream, and is primarily used for the logical processing of collections.
ParallelStream: Multiple pipes that provide parallel processing of streams. ParallelStream is another important feature of streams, and its underlying implementation is implemented using the Fork/Join framework
public class StreamTest {
@Test
public void testStream(a) {
List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
// Result: 1234567list.stream().forEach(System.out::print); }}Copy the code
public class ParallelStreamTest {
@Test
public void testParallelStream(a) {
List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
// Result: 5726134list.parallelStream().forEach(System.out::print); }}Copy the code
We can see that forEach is used directly by parallelStream to traverse data out of order.
What if you want parallelStream traversals to be sequential?
public class ParallelStreamTest {
@Test
public void testParallelStream(a) {
List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
// Result: 1234567list.parallelStream().forEachOrdered(System.out::print); }}Copy the code
ParallelStream works by:
A case study in practice
1. Find the same element in both sets. This mode is used to import data in batches. Query data before adding or modifying data in batches.
public class WorkTest {
@Test
public void testWork1(a) {
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());
// Result: [a, ab]System.out.println(collect); }}Copy the code
2. Set a and B, filter out the elements in set A but not in set B. This situation can be used if you specify an ID set, query the data set from the database according to the ID set, and filter out the ids that do not exist in the data set according to the ID set, and these ids need to be added.
@Test
public void testWork2(a) {
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());
// Result: [b]
System.out.println(collect);
}
Copy the code
3. Filter data based on conditions and redo data transformations
@AllArgsConstructor
@Data
class User {
private String name;
private Integer age;
}
@Test
public void testWork3(a) {
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);
}
Copy the code
4. Count the age of the youngest person with the same name in the specified set
@Test
public void testWork4(a) {
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);
// Result: name: Sue,age:29
// name:tom,age:23
// name:james,age:28
System.out.println("name:" + name + ",age:" + user.getAge());
});
}
Copy the code
One last word (attention, don’t fuck me for nothing)
If this article is of any help or inspiration to you, please pay attention to it. Your support is the biggest motivation for me to keep writing.
Ask for a key three even: like, forward, look.
In addition to pay attention to the public number: [Su SAN said technology], in the public number reply: interview, code artifact, development manual, time management have super good fan welfare, in addition reply: add group, can communicate with a lot of BAT big factory seniors and learn.