Before learning how to use this function, we must first know the meaning of the parameter of this function.
Basic use
Let me give you a simple example:
Algorithm Question: Words
Question description
Each sentence consists of multiple words, and the length of each word in the sentence may be different. Let's assume that the length of each word Ni is the weight of that word, all you need to do is give the average weight V of the entire sentence.Answer the requirements
Time limit: 1000ms, memory limit: 100MB
enter
The input has only one line, containing a string S (the length will not exceed 100), representing the entire sentence, which only contains upper and lower case English letters, and there is a space between each word.Output
The average weight V of the sentence S is output (rounded to retain two decimal places).Who Love Solo
Output sample
3.67
The meaning of this question is to find the average length of each word in a sentence. We can find the total length and divide it by the number of words. We can just use the reduce() method.
public class Demo { public static void main(String[] args) { Scanner sc = new Scanner(); String[] s = ().split(" "); double res = (s).mapToDouble(a ->()).reduce(0,(a,b)->a+b); (("%.2f",res/)); } }
In the code, .reduce(0,(a,b)->a+b);
This is our classic use case. We must first understand the meaning of a and b, and then learn how to use it.
Key concepts: Definition of initial value (Identity), accumulator (Accumulator), Combiner (Combiner)
- Identity: Defines an element that represents the initial value of the merge operation. If the Stream is empty, it is also the default result of the Stream.
- Accumulator: Defines a function with two parameters. The first parameter is the return value of the previous merge function, and the second is the next element in the Strem.
- Combiner: Call a function to combine the results of the merge operation. This function will be called when the merge is executed in parallel or when the accumulator function and the implementation type of the accumulator do not match.
That is to say0
It is our initial value.(a,b)->a+b
It is our accumulator, among whicha
It's the result of the last calculation.b
It is the current element in the Stream stream and the following onea+b
It is the calculation rule, such as if we change it toa*b
, that is, we calculate the product, of course we can also use method reference instead of lambda expressions.
double res = (s).mapToDouble(a ->()).reduce(0,Double::sum);
This is the most basic use. I wonder if you guys have learned it?
Additional examples
Of course, we can use the reduce method to process other types of streams. For example, we can operate an array of String and splice the strings of the array.
List<String> letters = ("a", "b", "c", "d", "e"); String result = letters .stream() .reduce("", (partialString, element) -> partialString + element); assertThat(result).isEqualTo("abcde");
You can also use method references to simplify the code
String result = ().reduce("", String::concat); assertThat(result).isEqualTo("abcde");
Let's change the above example of splicing strings to the requirements, first convert the string to capital and then splice it
String result = letters .stream() .reduce( "", (partialString, element) -> () + ()); assertThat(result).isEqualTo("ABCDE");
In addition, we can merge elements in parallel (parallel merge, as will be explained in detail below), and merge an array of numbers in parallel to sum
List<Integer> ages = (25, 30, 45, 28, 32); int computedAges = ().reduce(0, a, b -> a + b, Integer::sum);
When operating in parallel on a stream, the stream is divided into multiple substreams at runtime to operate in parallel. In the above example, we need a function to combine the results returned by each substream, which is the one mentioned aboveCombiner
(Combinator).
There is a point of attention, the following code cannot be compiled
List<User> users = (new User("John", 30), new User("Julie", 35)); int computedAges = ().reduce(0, (partialAgeResult, user) -> partialAgeResult + ());
The reason why the above code cannot be compiled is that the stream contains the User object, but the parameters of the accumulative function are numbers and user objects, and the implementation of the accumulator is summed, so the compiler cannot infer the type of the parameter user. You can change the code to the following and compile it
int result = () .reduce(0, (partialAgeResult, user) -> partialAgeResult + (), Integer::sum); assertThat(result).isEqualTo(65);
When the parameters of the stream or accumulator are sequentially read to match the types of its implementation, we do not need to use a combiner.
Parallel reading stream
As mentioned above, we can use the reduce() method in parallel. When using it in parallel, you should pay attention to the following points:
- The result has nothing to do with the order of processing
- Operation does not affect the original data
- The operation has no status and the same input, and the output result is the same.
We pay attention to the above 3 points in case of unintended results. Generally, we process streams containing a large amount of data or time-consuming operations in parallel.
Handle exceptions
In the above example, the reduce method does not throw an exception. If an exception occurs, how should we handle the exception gracefully? See the following example:
List<Integer> numbers = (1, 2, 3, 4, 5, 6); int divider = 2; int result = ().reduce(0, a / divider + b / divider);
If divider = 0 , it will be thrownArithmeticException
, In this case, the general processing method uses try/catch to catch exceptions
public static int divideListElements(List<Integer> values, int divider) { return () .reduce(0, (a, b) -> { try { return a / divider + b / divider; } catch (ArithmeticException e) { (, "Arithmetic Exception: Division by Zero"); } return 0; }); }
If using try/catch directly will affect the readability of the code, we can encapsulate the divide operation into a separate method and catch exceptions in it, as follows:
rivate static int divide(int value, int factor) { int result = 0; try { result = value / factor; } catch (ArithmeticException e) { (, "Arithmetic Exception: Division by Zero"); } return result }
divideListElements Call divide method
public static int divideListElements(List<Integer> values, int divider) { return ().reduce(0, (a, b) -> divide(a, divider) + divide(b, divider)); }
Processing of complex objects
We can use the reduce method to process complex objects. Reduce needs to accept identity, accumulator, and combiner corresponding to complex objects.
Suppose a scenario: calculate the rating of a website user, which is the average of all comments from all users.
There is a class Review defined as follows:
public class Review { private int points; private String review; // constructor, getters and setters }
Class Rating Reference Review Calculates user ratings
public class Rating { double points; List<Review> reviews = new ArrayList<>(); public void add(Review review) { (review); computeRating(); } private double computeRating() { double totalPoints = ().map(Review::getPoints).reduce(0, Integer::sum); = totalPoints / (); return ; } public static Rating average(Rating r1, Rating r2) { Rating combined = new Rating(); = new ArrayList<>(); (); (); return combined; } }
Assemble some user and user comments first
User john = new User("John", 30); ().add(new Review(5, "")); ().add(new Review(3, "not bad")); User julie = new User("Julie", 35); ().add(new Review(4, "great!")); ().add(new Review(2, "terrible experience")); ().add(new Review(4, "")); List<User> users = (john, julie);
Call the reduce method to process the score
Rating averageRating = () .reduce(new Rating(), (rating, user) -> (rating, ()), Rating::average);
I wonder if you have learned it?
Summarize
This is all about this article about the usage of Java(). For more related usage of () please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!