New to Java8 are Lambda expressions and streams. When used together, streams and Lambda expressions can make code concise and readable because of the declarative nature of streams that handle data sets
How does flow simplify code
If there is a requirement to perform a processing on the dishes found in the database:
- Screen for dishes with fewer than 400 calories
- Make a ranking of the selected dishes
- Gets the name of the sorted dish
Cuisine: Dish. Java
public class Dish {
private String name;
private boolean vegetarian;
private int calories;
private Type type;
// getter and setter
}
Copy the code
How Java8 was implemented before
private List<String> beforeJava7(List<Dish> dishList) { List<Dish> lowCaloricDishes = new ArrayList<>(); //1. Select dishes with fewer than 400 caloriesfor (Dish dish : dishList) {
if(dish.getCalories() < 400) { lowCaloricDishes.add(dish); Sort (lowCaloricDishes, new Comparator<Dish>() {@override public int compare(Dish o1, Dish o2) {returnInteger.compare(o1.getCalories(), o2.getCalories()); }}); List<String> lowCaloricDishesName = new ArrayList<>();for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
Copy the code
Implementation after Java8
private List<String> afterJava8(List<Dish> dishList) {
returnDishlist.stream ().filter(d -> d. calorie () < 400) // Select dishes with calories less than 400. Sorted (Comparing (Dish:: getcalorie)) // Sort by calories .map(Dish::getName) // Collect (Collectors. ToList ()); // Convert to List}Copy the code
It’s easy to do things in five lines that used to take 24 lines of code
- Classify the dishes queried in the database according to the types of dishes and return one
Map<Type, List<Dish>>
The results of the
This would have been creepy before JDK8
How Java8 was implemented before
private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) {
Map<Type, List<Dish>> result = new HashMap<>();
for(Dish Dish: dishList) {// Initialize if not presentif (result.get(dish.getType())==null) {
List<Dish> dishes = new ArrayList<>();
dishes.add(dish);
result.put(dish.getType(), dishes);
} else{// add result.get(dish.getType()).add(dish); }}return result;
}
Copy the code
Fortunately, JDK8 has streams, so you don’t have to worry about complex collection handling requirements anymore
Java8 after the implementation
private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) {
return dishList.stream().collect(groupingBy(Dish::getType));
}
Copy the code
See the power of streams. We’ll cover streams in more detail
What is the flow
A stream is a sequence of elements generated from a source that supports data processing operations. The sources can be arrays, files, collections, or functions. A stream is not a collection element, it is not a data structure, it does not hold data, and its primary purpose is computation
How to generate a stream
There are five main ways to generate streams
- Through collection generation, one of the most common applications
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();
Copy the code
A stream is generated through the stream method of the collection
- From an array
int[] intArr = new int[]{1, 2, 3, 4, 5}; IntStream stream = Arrays.stream(intArr); Copy the code
The arrays. stream method generates streams that are numeric streams rather than stream
. The use of numerical flow can avoid unpacking during calculation and improve performance. The Stream API provides mapToInt, mapToDouble, and mapToLong methods for converting a Stream into a numeric Stream. It also provides a boxed method for converting a numeric Stream into an object Stream
- By value generation
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); Copy the code
A Stream is generated by the of method of a Stream, and an empty Stream is generated by the empty method of a Stream
- File generation
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset()) Copy the code
A stream is obtained using the files.line method, and each resulting stream is a row in a given file
- Provided through function generation
iterate
andgenerate
Two static methods generate streams from functions- iterator
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5); Copy the code
iterate
The method takes two arguments, the first being the initialization value and the second being the function operation to perform, becauseiterator
The generated stream is an infinite stream and passeslimit
Methods The flow is truncated and only 5 even numbers are generated- generator
Stream<Double> stream = Stream.generate(Math::random).limit(5); Copy the code
generate
The method takes one parameter of typeSupplier<T>
, which provides the value for the stream.generate
The resulting stream is also infinite and therefore passeslimit
The convection was truncated
The operation type of the flow
There are two main types of operations for streams
- Intermediate operations A stream can be followed by zero or more intermediate operations. The main purpose is to open the stream, do some degree of data mapping/filtering, and then return a new stream to be used by the next operation. These types of operations are inert, and simply calling these methods does not actually start the stream traversal. The actual traversal will have to wait for terminal operations. Common intermediate operations are described below
filter
,map
Etc. - Terminal operation A stream can have only one terminal operation. After this operation is performed, the stream is closed and cannot be operated again. Therefore, a stream can only be traversed once. It is the execution of the terminal operation that really begins the stream traversal. As will be described below
count
,collect
Etc.
Flow using
The use of streams will be divided into terminal operations and intermediate operations
In the middle of operation
The filter screen
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
Copy the code
Filter method is used to filter conditions. The parameter of filter method is a condition
Distinct Removes duplicate elements
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();
Copy the code
Use the DISTINCT method to quickly remove duplicate elements
Limit Returns the specified number of streams
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);
Copy the code
The limit method specifies the number of streams to return. Limit must be >=0, otherwise an exception will be thrown
Skip Skips elements in the stream
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);
Copy the code
Skip the elements in the stream by skipping the first two elements in the example above, so it prints 2,3,4,5. Skip must be >=0, otherwise an exception will be thrown
The map flow mapping
A flow map is a mapping of an accepted element to another element
List<String> stringList = Arrays.asList("Java 8"."Lambdas"."In"."Action");
Stream<Integer> stream = stringList.stream().map(String::length);
Copy the code
The mapping can be done using the map method. This example completes the mapping of String -> Integer. The previous example completed the mapping of Dish->String using the map method
FlatMap flow conversion
Converts every value in one stream to another
List<String> wordList = Arrays.asList("Hello"."World");
List<String> strList = wordList.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
Copy the code
Map (w -> w.split(” “)) returns Stream
. If we want to get Stream
, we can convert Stream
->Stream
to flatMap
Match elements
Three matching methods are provided
- AllMatch matches all
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().allMatch(i -> i > 3)) { System.out.println("All values greater than 3"); } Copy the code
Through allMatch method
- AnyMatch matches one of them
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().anyMatch(i -> i > 3)) { System.out.println("There are values greater than 3."); } Copy the code
Is equivalent to
for (Integer i : integerList) { if (i > 3) { System.out.println("There are values greater than 3."); break; }}Copy the code
Prints if there is a value greater than 3, which is implemented in java8 through the anyMatch method
- NoneMatch all don’t match
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().noneMatch(i -> i > 3)) { System.out.println("All values less than 3"); } Copy the code
through
noneMatch
Method implementation
Terminal operation
Count the number of elements in the flow
- By the count
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Long result = integerList.stream().count(); Copy the code
Count the number of elements in the stream by using the count method
- Through counting
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Long result = integerList.stream().collect(counting()); Copy the code
This last method of counting the number of elements is particularly useful when used in conjunction with COLLECT
To find the
There are two ways to find it
-
FindFirst finds the first one
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst(); Copy the code
Find the first element greater than three using the findFirst method and print it
-
FindAny Finds a random one
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny(); Copy the code
The findAny method finds one element greater than three and prints it. For internal optimization reasons, it ends when it finds the first element greater than three, which gives the same result as the findFirst method. The findAny method is provided to make better use of parallel streams. The findFirst method is more limited in parallelism.
Reduce groups elements in the flow
Suppose we sum the values of a set
-
Jdk8 before
int sum = 0; for (int i : integerList) { sum += i; } Copy the code
-
Jdk8 is processed by reduce
int sum = integerList.stream().reduce(0, (a, b) -> (a + b)); Copy the code
This can be done in a single line, or you can use the method reference shorthand:
int sum = integerList.stream().reduce(0, Integer::sum); Copy the code
Reduce takes two parameters, an initial value of 0 in this case, and a BinaryOperator
Accumulator to combine the two elements to produce a new value, as well as an overloaded method with no initialization value
Gets the minimum maximum value in the stream
- Obtain the minimum and maximum value by min/ Max
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo); Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo); Copy the code
It can also be written as:
OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min(); OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max(); Copy the code
min
Get the minimum value in the stream,max
To obtain the maximum value in the stream, the method parameter isComparator<? super T> comparator
- Get the minimum and maximum values by 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)); Copy the code
minBy
Get the minimum value in the stream,maxBy
To obtain the maximum value in the stream, the method parameter isComparator<? super T> comparator
- Obtain the minimum and maximum value through reduce
Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min); Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max); Copy the code
sum
- Through summingInt
int sum = menu.stream().collect(summingInt(Dish::getCalories)); Copy the code
If the data type is
double
,long
bysummingDouble
,summingLong
Method to sum - Through the reduce
int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum); Copy the code
- Through the sum
int sum = menu.stream().mapToInt(Dish::getCalories).sum(); Copy the code
When summing, maximizing, and minimizing above, there are different ways to choose to perform the same operation. The methods can be Collect, reduce, and min/ Max /sum. The min, Max, and sum methods are recommended. Because it is the most concise and readable, mapToInt converts the object stream to a numeric stream, avoiding boxing and unboxing operations
Average by averagingInt
double average = menu.stream().collect(averagingInt(Dish::getCalories));
Copy the code
If the data type is double and long, averagingDouble and averagingLong are used to average
Summation, average, maximum, and minimum values are computed by summarizingInt
IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories)); double average = intSummaryStatistics.getAverage(); / / get average int min = intSummaryStatistics. GetMin(a); / / get the minimum int Max = intSummaryStatistics. GetMax (); / / get the maximum long sum = intSummaryStatistics. GetSum (); // Get the sumCopy the code
If the data type is summarizingDouble or summarizingLong, the summarizingDouble or summarizingLong methods are used
Element traversal via foreach
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
integerList.stream().forEach(System.out::println);
Copy the code
While implementing traversal before JDK8:
for (int i : integerList) {
System.out.println(i);
}
Copy the code
In jdk8, iterating through elements is much easier. For-each can be implemented directly through foreach
Returns the collection
List<String> strings = menu.stream().map(Dish::getName).collect(toList());
Set<String> sets = menu.stream().map(Dish::getName).collect(toSet());
Copy the code
Just a few examples, there are many other methods before JDK8
List<String> stringList = new ArrayList<>();
Set<String> stringSet = new HashSet<>();
for (Dish dish : menu) {
stringList.add(dish.getName());
stringSet.add(dish.getName());
}
Copy the code
The use of traversal and return collections to discover that a flow simply brings the original external iteration inside, which is one of the main features of a flow. Internal iteration can reduce a lot of code
Joining elements in a stream through joining
String result = menu.stream().map(Dish::getName).collect(Collectors.joining(","));
Copy the code
By default, if the map method is not used to process the string returned by the toString method of joining, the parameter of the joining method is the delimiter of the element. If the string is not specified, it will be a string, which is not readable
Advancements are grouped by groupingBy
Map<Type, List<Dish>> result = dishList.stream().collect(groupingBy(Dish::getType));
Copy the code
GroupingBy is passed into the collect method for grouping, where the method parameter of groupingBy is the classification function. You can also use groupingBy for multiple levels of classification by nesting
Map<Type, List<Dish>> result = menu.stream().collect(groupingBy(Dish::getType,
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
})));
Copy the code
Advanced partitioning is done by partitioningBy
Partitions are special groups that are classified according to true and false, so the results returned can be divided into up to two groups
Map<Boolean, List<Dish>> result = menu.stream().collect(partitioningBy(Dish :: isVegetarian))
Copy the code
Is equivalent to
Map<Boolean, List<Dish>> result = menu.stream().collect(groupingBy(Dish :: isVegetarian))
Copy the code
This example may not tell the difference between partitioning and sorting, and may not even be necessary at all.
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));
Copy the code
The key of the returned value is still of Boolean type, but it is classified by range. Partitioning is better for handling sorting by range
conclusion
By using the Stream API, you can simplify your code and improve the readability of your code. Before I learned the Stream API, I wanted to kick anyone who wrote a lot of lambdas in my application. I think I might be in love with him now. Don’t mix declarative programming with imperative programming. A few days ago, brush segment:
imango
Don’t use declarative programming syntax to do imperative programming