There are two types of operations for streams:

  • Intermediate: A flow can be followed by zero or more Intermediate operations. The main goal is to open streams, do some sort of data mapping/filtering, and then return a new stream for the next operation to use. These operations are lazy, that is, they are called to the method without actually starting the flow.

Map (mapToInt, flatMap, etc.), filter, distinct, sorted, peek, LIMIT, skip, parallel, sequential, and unordered

  • Terminal: A stream can have only one Terminal operation. When this operation is performed, the stream is “light” and can no longer be manipulated. So this must be the last operation of the stream. The execution of the Terminal operation will actually start traversing the stream and will produce a result, or a side effect

ForEach, forEachOrdered, toArray, Reduce, Collect, min, Max, Count, anyMatch, allMatch, noneMatch, findFirst, findAny, forEach ordered, toArray, Reduce, Collect, Min, Max, Count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator

  • Short-circuiting: Returns a finite amount of data for an infinite and infinite set

AnyMatch, allMatch, noneMatch, findFirst, findAny, LIMIT

Filter

Object matching filtering

methods schematic
filter Return a stream containing all matched objects by passing an expected match as an argument.
distinct Returns a stream containing unique elements (uniqueness depends on how element equality is implemented).
limit Returns a stream with a specified upper bound
skip Returns a stream discarding the first n elements
List expensiveInvoices
= invoices.stream()
.filter(inv -> inv.getAmount() > 10_000)
.limit(5)
.collect(Collectors.toList());
Copy the code

Matching:

A match is a common data processing pattern that determines whether a given attribute is matched. The expression returns a Boolean at the end

methods schematic
allMacth Each of the stream objects should be matched to
anyMatch The stream object is interrupted if there is a match
noneMatch The flow object has no matching object
boolean expensive =
invoices.stream()
.allMatch(inv -> inv.getAmount() > 1 _000);
Copy the code

Finding

The: stream interface also provides options like findFirst and findAny to pull arbitrary elements from the stream. They can be connected to methods like filter. Both findFirst and findAny return an optional object. In the serial stream, both return the first object. In the parallel stream, findAny returns the data that was processed fastest by the first thread

methods schematic
findFirst Returns the first matched object
findAny Returns the first matched object
Optional =
invoices.stream()
.filter(inv ->
inv.getCustomer() == Customer.ORACLE)
.findAny();
Copy the code

Mapping

: Streams support mapping methods, passing a function object as a method to convert elements in the stream to another type. This approach applies to a single element and maps it to a new element.

methods schematic
map Applies to a single element to map it to a new element.
List ids
= invoices.stream()
.map(Invoice::getId)
.collect(Collectors.toList());
Copy the code

Reducing

methods schematic
reduce(BinaryOperator) One function is primarily used to maximize the sum.
int product = numbers.stream().reduce(1, (a, b) -> a * b);
int max = numbers.stream().reduce(Integer.MIN_VALUE,
Integer::max);
Copy the code

The forEach, peek

ForEach Terminal operations no longer return stream objects. ForEach loops cannot modify the value of local variables they contain. ForEach loops cannot prematurely end with keywords such as break/return. Peek can re-encapsulate a loop object and return a new one

sorted

Instead of sorting an array, you can use maps, filter, LIMIT, SKIP, or even distinct to reduce the number of elements in the Stream first, and then sort the Stream. This will help the program to significantly shorten the execution time.

List<Person> persons = new ArrayList();
 for (int i = 1; i <= 5; i++) {
 Person person = new Person(i, "name" + i);
 persons.add(person);
 }
List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
System.out.println(personList2);
Copy the code

min/max/distinct(Terminal)

Get the minimum value, maximum value and de-repetition of the final calculation

Collectors

All the methods you’ve seen so far have been to return another stream or a value of type Boolean,int, or an optional object. In contrast, the collect method is a closing operation that aggregates all elements in the stream into a summary result. Pass to collect method parameter is a Java util. Stream. The Collector type of object. The Collector object actually defines a method for aggregating elements from the flow into the final result. Initially, the factory method yles.tolist () was used to return a Collector object that described how to turn the flow into a List. Many similar Collectors have been built into the Collectors class. For example, you can group invoices by consumer using the oughter.groupingby method

Map<Customer, List> customerToInvoices
= invoices.stream().collect(Collectors.group
ingBy(Invoice::getCustomer));
Copy the code

groupingBy/partitioningBy

Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
 limit(100).
 collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
 Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
 System.out.println("Age " + persons.getKey() + "=" + persons.getValue().size());
}
Copy the code

After using the condition “age < 18” for grouping, you can see that minors under 18 are one group and adults are another. PartitioningBy is a special type of groupingBy, which constructs the returned data structure according to whether the condition tests two results, get(true) and get(false) can be all the element objects.

The input and output processed by map correspond to each other

methods schematic
mapToInt The body of the function returns an int
mapToDouble The body of the function returns type Double.
mapToLong The body of the function returns a value of type Long.

The flatMap processes input and outPut in a one-to-many relationship

methods schematic
flatMapToInt The body of the function returns an int
flatMapDouble The body of the function returns type Double.
flatMapLong The body of the function returns a value of type Long.
Stream<List<Integer>> inputStream = Stream.of(
 Arrays.asList(1),
 Arrays.asList(2.3),
 Arrays.asList(4.5.6)); Stream<Integer> outputStream = inputStream. flatMap((childList) -> childList.stream());Copy the code

Parallel Streams

The Stream API supports convenient data parallelism. In other words, you can explicitly let the stream pipe run in parallel without worrying about the underlying implementation. Behind the scenes, the Stream API uses the Fork/Join framework to take full advantage of your machine’s multi-core architecture. All you need to do is replace the stream() method with the parallelStream() method. Not all are in parallel, as follows

  • The internal implementation of Parallel Streams depends on how easy it is to divide data structures into pieces that can be used by different threads. Data structures like arrays are easy to partition, while data structures like lists or files are hard to partition
  • The higher the cost of a single element in the computation flow, the more sense it makes to apply parallelism.
  • Use raw data types if possible, which use less memory and have a higher cache hit ratio.
  • The larger the data volume of the elements in the stream, the better, because the cost of parallelization is spread across all the elements, and the time savings of parallelization are relatively greater. Of course, this is also related to the cost of the individual element calculation.
  • In general, the more cores, the better.