Lambda expression traverses Collection collection
Java8 adds a default method forEach(Consumer action) to the Iterable interface. The type of parameter required by this method is a functional interface, and the Iterable interface is the parent interface of the Collection interface, so the Collection collection can also directly call this method.
When the program calls forEach(Consumer action) of Iterable to traverse the collection elements, the program will pass the collection elements to the accept(T t) method of Consumer (the only abstract method in this interface) in turn. Because Consumer is a functional interface, Lambda expressions can be used to iterate over collection elements.
The following program demonstrates the use of Lambda expressions to iterate over collection elements.
public class CollectionEach { public static void main(String[] args) { // create a collection Collection objs = new HashSet(); objs.add("C Language Chinese Network Java Tutorial"); objs.add("C Language Chinese Network C Language Tutorial"); objs.add("C Language Chinese Network C++ Tutorial"); // Call the forEach() method to traverse the collection objs.forEach(obj -> System.out.println("Iterate collection elements:" + obj)); } }
The output is:
Iterating Collection Elements: C Language Chinese Network C++ Tutorial
Iterating Collection Elements: C Language Chinese Network C Language Tutorial
Iterating Collection Elements: C Language Chinese Network Java Tutorial
The code in bold in the above program calls the default method of forEach() of Iterable to traverse the collection elements. The parameter passed to this method is a Lambda expression whose target type is Consumer. The forEach() method will automatically pass the collection elements one by one to the formal parameters of the Lambda expression, so that the code body of the Lambda expression can traverse to the collection elements.
Use foreach loop to traverse Collection collection
In addition to using the Iterator interface to iterate and access the elements in the Collection collection, we can also use the foreach loop provided by Java5 to iterate and access the collection elements, which is more convenient. The following program demonstrates the use of the foreach loop to iterate through the elements of a collection.
public class ForeachTest { public static void main(String[] args) { // create a collection Collection objs = new HashSet(); objs.add("C Language Chinese Network Java Tutorial"); objs.add("C Language Chinese Network C Language Tutorial"); objs.add("C Language Chinese Network C++ Tutorial"); for (Object obj : objs) { // The obj variable here is not the collection element itself String obj1 = (String) obj; System.out.println(obj1); if (obj1.equals("C Language Chinese Network Java Tutorial")) { // The following code will throw ConcurrentModificationException exception objs. remove(obj); } } System.out.println(objs); } }
The output is:
C Language Chinese Network C++ Tutorial
C Language Chinese Network C Language Tutorial
C Language Chinese Network Java Tutorial
[C Language Chinese Website C++ Tutorial, C Language Chinese Website C Language Tutorial]
The above code uses the foreach loop to iterate and access the elements in the Collection collection more concisely, which is the advantage brought by the foreach loop of JDK 1.5. Similar to using the Iterator interface to iteratively access collection elements, the iteration variable in the foreach loop is not the collection element itself. The system just assigns the values of the collection elements to the iteration variable in turn, so there is no need to modify the value of the iteration variable in the foreach loop. practical significance.
Likewise, when using a foreach loop to iterate through elements of a collection, the collection cannot be modified, otherwise a ConcurrentModificationException will be thrown. So the exception will be thrown at line 14 of the above program.
Use Java 8’s new Predicate to operate Collection collection
Since Java 8, a removeIf(Predicate filter) method has been added to the Collection collection, which will delete all elements that meet the filter conditions in batches. This method requires a Predicate object as a parameter, and Predicate is also a functional interface, so you can use a Lambda expression as a parameter.
The following program demonstrates the use of Predicates to filter collections.
public class ForeachTest { public static void main(String[] args) { // create a collection Collection objs = new HashSet(); objs.add(new String("C Language Chinese Network Java Tutorial")); objs.add(new String("C Language Chinese Network C++ Tutorial")); objs.add(new String("C Language Chinese Network C Language Tutorial")); objs.add(new String("C Language Chinese Network Python Tutorial")); objs.add(new String("C Language Chinese Network Go Tutorial")); // Filter the collection using a Lambda expression (target type is Predicate) objs. removeIf(ele -> ((String) ele). length() < 12); System.out.println(objs); } }
Line 11 of the above program calls the removeIf() method of the Collection collection to delete eligible elements in the collection in batches, and the program passes in a Lambda expression as a filter condition. All string elements with length less than 12 are removed. Compile and run this code, you can see the following output:
[C language Chinese website Java tutorial, C language Chinese website Python tutorial]
Using Predicate can fully simplify the calculation of collections. Assuming that there is still the objs collection shown in the above program, if the program has the following three statistical requirements:
- Statistically count the number of “C language Chinese website” strings in the collection.
- The number of occurrences of the “Java” string in the statistics collection.
- The number of strings whose length is greater than 12 appears in the statistical collection.
This is just a hypothesis, in fact there may be more statistical needs. If you use the traditional programming method to complete these requirements, you need to execute three loops, but you only need one method with Predicate. The following code demonstrates this usage.
public class ForeachTest2 { public static void main(String[] args) { // create a collection Collection objs = new HashSet(); objs.add(new String("C Language Chinese Network Java Tutorial")); objs.add(new String("C Language Chinese Network C++ Tutorial")); objs.add(new String("C Language Chinese Network C Language Tutorial")); objs.add(new String("C Language Chinese Network Python Tutorial")); objs.add(new String("C Language Chinese Network Go Tutorial")); // Count the number of "C Language Chinese Network" strings appearing in the collection System.out.println(calAll(objs, ele -> ((String) ele).contains("C Language Chinese Network"))); // Count the number of occurrences of the "Java" string in the collection System.out.println(calAll(objs, ele -> ((String) ele).contains("Java"))); // Count the number of strings whose length is greater than 12 in the statistics collection System.out.println(calAll(objs, ele -> ((String) ele).length() > 12)); } public static int calAll(Collection books, Predicate p) { int total = 0; for (Object obj : books) { // Use the test() method of Predicate to judge whether the object meets the conditions specified by Predicate if (p.test(obj)) { total + + ; } } return total; } }
The output is:
5 1 1
The above program first defines a calAll() method, which uses Predicate to judge whether each collection element meets a specific condition, and the condition will be dynamically passed in through the Predicate parameter. From lines 11, 13, and 15 of the above program, we can see that the program passes in 3 Lambda expressions whose target types are all Predicates, so the calAll() method will only count books that meet the Predicate conditions.
Use Java 8’s new Stream to operate Collection collection
Java8 also adds streaming APIs such as Stream, IntStream, LongStream, DoubleStream, etc. These APIs represent multiple elements that support serial and parallel aggregate operations. Among the above four interfaces, Stream is a general stream interface, while IntStream, LongStream, and DoubleStream represent streams whose element types are int, long, and double.
Java 8 also provides corresponding Builders for each of the above streaming APIs, such as Stream.Builder, IntStream.Builder, LongStream.Builder, DoubleStream.Builder, developers can use these Builders to create corresponding streams.
The steps to use Stream independently are as follows:
- Use the builder() class method of Stream or XxxStream to create the corresponding Builder of the Stream.
- Repeated calls to the Builder’s add() method add multiple elements to the stream.
- Call the Builder’s build() method to get the corresponding Stream.
- Call the Stream’s aggregate method.
In the above 4 steps, different methods can be called in step 4 according to specific needs. Stream provides a large number of aggregation methods for users to call. For details, please refer to the API documentation of Stream or XxxStream. For most aggregation methods, each Stream can only be executed once. For example, the following program.
public class IntStreamTest { public static void main(String[] args) { IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build(); // The following code that calls the aggregation method can only be executed one line at a time System.out.println("is the maximum value of all elements: " + is.max().getAsInt()); System.out.println("is the minimum value of all elements: " + is.min().getAsInt()); System.out.println("is the sum of all elements: " + is.sum()); System.out.println("is the total of all elements:" + is.count()); System.out.println("is the average of all elements: " + is.average()); System.out.println("is whether the squares of all elements are greater than 20: " + is.allMatch(ele -> ele * ele > 20)); System.out.println("is whether the square of any element is greater than 20: " + is.anyMatch(ele -> ele * ele > 20)); // Map is to a new Stream, and each element of the new Stream is 2 times the original Stream element + 1 IntStream newIs = is.map(ele -> ele * 2 + 1); // Use method references to traverse collection elements newIs.forEach(System.out::println); // output 41 27 -3 37 } }
The above program first creates an IntStream, and then calls the aggregation method of the IntStream multiple times to perform operations, so that the relevant information of the stream can be obtained. Note: The above 5~13 lines of code can only execute one line at a time, so other codes need to be commented out.
Stream provides a large number of methods for aggregation operations, these methods can be “intermediate” (intermediate) or “terminal” (terminal).
- Intermediate methods: Intermediate operations allow the stream to remain open and allow subsequent methods to be called directly. The map() method in the above program is the intermediate method. The return value of the intermediate method is another stream.
- Terminal Method: The terminal method is the final operation of the convection. When a terminal method is executed on a Stream, the stream will be “consumed” and no longer usable. The sum(), count(), average() and other methods in the above program are terminal methods.
In addition, the method of streaming has the following two characteristics.
- Stateful method: This method adds some new properties to the stream, such as the uniqueness of elements, the maximum number of elements, and the guarantee that elements are processed in an orderly manner. Stateful methods tend to require greater performance overhead.
- Short-circuit method: The short-circuit method can end the convection operation as soon as possible without checking all elements.
The following is a brief introduction to the commonly used intermediate methods of Stream.
Method | Description |
---|---|
filter(Predicate predicate) | Filter Stream All elements that do not conform to the predicate |
mapToXxx(ToXxxFunction mapper) | Use ToXxxFunction to perform a one-to-one transformation on the elements in the stream, and the method returns the new The stream contains all elements produced by ToXxxFunction transformations. |
peek(Consumer action) | Perform some operations on each element in turn, and the stream returned by this method contains the same elements as the original stream. This method is mainly used for debugging. |
distinct() | This method is used to sort all repeated elements in the stream (the criterion for judging element duplication is to use equals() to compare and return true) . This is a stateful method. |
sorted() | This method is used to ensure that the elements in the stream are in an ordered state in subsequent accesses. This is a stateful method. |
limit(long maxSize) | This method is used to ensure the maximum number of elements allowed in subsequent accesses to the stream. This is a stateful, short-circuit method. |
The following is a brief introduction to the commonly used terminal methods of Stream.
Method | Description |
---|---|
forEach(Consumer action) | traversal stream All elements, execute action for each element |
toArray() | Convert all elements in the stream to an array |
reduce() | There are three overloaded versions of this method, all of which are used to combine elements in the stream by some operation |
min() | Returns the minimum value of all elements in the stream |
max() | Returns the minimum value of all elements in the stream Maximum |
count() | returns the count of all elements in the stream |
anyMatch(Predicate predicate) | Determine whether the stream contains at least one element that meets the Predicate condition. |
allMatch(Predicate predicate) | Determine whether each element in the stream meets the Predicate condition |
noneMatch(Predicate predicate) | Determine whether all elements in the stream do not meet the Predicate condition |
findFirst() | Return stream The first element in |
findAny() | returns any element in the stream |