Use Lambda expressions, foreach loops, Predicate operations, and Stream operations to traverse Collection collections

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:

  1. Statistically count the number of “C language Chinese website” strings in the collection.
  2. The number of occurrences of the “Java” string in the statistics collection.
  3. 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:

  1. Use the builder() class method of Stream or XxxStream to create the corresponding Builder of the Stream.
  2. Repeated calls to the Builder’s add() method add multiple elements to the stream.
  3. Call the Builder’s build() method to get the corresponding Stream.
  4. 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.

< /table>

In addition, Java 8 allows the use of streaming APIs to operate collections. The Collection interface provides a default method of stream(), which returns the stream corresponding to the collection, and then the collection elements can be manipulated through the streaming API. Stream greatly enriches the functionality of collections because it can aggregate the elements of the collection as a whole.

For example, for the sample program in the Predicate operation collection section, it is necessary to define an additional calAll() method to traverse the collection elements, and then judge each collection element in turn, which is too troublesome. If you use Stream, you can directly perform batch operations on all the elements in the collection. Let’s rewrite this program using Stream.

public class CollectionStream {
    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(objs.stream().filter(ele -> ((String) ele).contains("C Language Chinese Network")).count()); // output 5
        // Count the number of occurrences of the "Java" string in the collection
        System.out.println(objs.stream().filter(ele -> ((String) ele).contains("Java")).count()); // output 1
        // Count the number of strings whose length is greater than 12 in the statistics collection
        System.out.println(objs.stream().filter(ele -> ((String) ele).length() > 12).count()); // output 1
        // First call the stream () method of the Collection object to convert the collection into a Stream
        // Then call the mapToInt() method of Stream to get the IntStream corresponding to the original Stream
        objs. stream(). mapToInt(ele -> ((String) ele). length())
                // Call the forEach() method to traverse each element in the IntStream
                .forEach(System.out::println);// output 11 11 12 10 14
    }
}

The output is:

5 1 1 11 11 12 10 14

From lines 11 to 20 of the above code, it can be seen that the program can return the Stream corresponding to the collection as long as it calls the stream() method of Collection, and then process all collection elements through the method provided by Stream, which greatly simplifies This is also the advantage of Stream programming.

Line 18 of the above program first calls the stream() method of the Collection object to convert the collection into a Stream object, and then calls the mapToInt() method of the Stream object to convert it into an IntStream mapToInt. method is an intermediate method, so the program can continue to call IntStream’s forEach() method to iterate over the elements in the stream.

syntaxbug.com © 2021 All Rights Reserved.
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