ABSTRACT: Origin http://peijie2016.oschina.io Welcome to reprint, keep the abstract, thank you!
Stream is an API introduced in Java 8 that heavily uses lambda expressions.
Stream can process data sets in a streaming way. Before Java 8, we need iterators to process these sets, such as iterators, which are external iterations. Stream is internal iterations. We don't need to care about how the internal elements of the set iterate. The computer will automatically help us choose the most suitable implementation.
How to create a stream
- Most commonly, there is a collection object List < String > STRs = Arrays. asList ("Java 8", "Lambdas", "In", "Action"); and a Stream < String > flow is obtained by calling strs.stream().
If you want to use parallel streams to increase performance, use strs.parallelStream(). - Create by value: Stream < String > stream = Stream. of ("Java 8", "Lambdas", "In", "Action");
- Created by an array:
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
- Created by a file:
// How many different words are there in the statistical text file
long uniqueWords = 0;
try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
} catch (IOException e) {
}
- Stream.iterate() and Stream.generate() can produce infinite flow, that is, there are infinite elements. Generally speaking, limit(n) should be used to restrict this flow in order to avoid generating an infinite number of elements.
public void iterator() {
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
}
public void generate() {
Stream.generate(Math::random).limit(5).forEach(System.out::println);
}
Stream common methods
The Stream API supports two types of operations: intermediate operations (such as filter or map) and terminal operations (such as count, findFirst, forEach, and reduce).
I use a filter menu requirement as an example.
Dead work
public class Dish {
private final String name;
private final boolean vegetarian;
private final int calories;
private final Type type;
public Dish(String name, boolean vegetarian, int calories, Type type) {
this.name = name;
this.vegetarian = vegetarian;
this.calories = calories;
this.type = type;
}
public String getName() {
return name;
}
public boolean isVegetarian() {
return vegetarian;
}
public int getCalories() {
return calories;
}
public Type getType() {
return type;
}
@Override
public String toString() {
return name;
}
}
public enum Type {MEAT, FISH, OTHER}
public List<Dish> init() {
return Arrays.asList(
new Dish("pork", false, 800, Type.MEAT),
new Dish("beef", false, 700, Type.MEAT),
new Dish("chicken", false, 400, Type.MEAT),
new Dish("french fries", true, 530, Type.OTHER),
new Dish("rice", true, 350, Type.OTHER),
new Dish("season fruit", true, 120, Type.OTHER),
new Dish("pizza", true, 550, Type.OTHER),
new Dish("prawns", false, 300, Type.FISH),
new Dish("salmon", false, 450, Type.FISH));
}
Filtering and screening
Predicate: Function returning boolean
- filter(): Accepts a predicate and returns a qualified set of elements
@Test
public void filter() {
List<Dish> menu = init();
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(Collectors.toList());
Assert.assertEquals(4, vegetarianMenu.size());
}
- distinct(): Returns a collection of distinct elements in the collection (de-duplicated)
@Test
public void distinct() {
List<Integer> numbers = Arrays.asList(5, 1, 2, 1, 3, 3, 2, 4);
numbers.stream().distinct().forEach(System.out::println);
}
- limit(): intercepts a specified number of elements in the stream and returns a stream that does not exceed a given length. If the flow is ordered, the first n elements will be returned at most.
@Test
public void limit() {
List<Dish> menu = init();
menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.forEach(System.out::println);
}
- skip(): skips the specified number of elements and returns a stream that discards the first n elements. If there are less than n elements in the flow, an empty flow is returned.
@Test
public void skip() {
List<Dish> menu = init();
menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.skip(2)
.forEach(System.out::println);
}
mapping
- map(): Accepts a function as a parameter. This function is applied to each element and mapped to a new element.
@Test
public void map() {
List<Dish> menu = init();
List<String> dishNames = menu.stream().map(m -> m.getName()).collect(Collectors.toList());
}
- flatMap(): Each value in a stream is replaced by another stream, and then all the streams are connected into one stream, that is, flattened into one stream.
@Test
public void flatMap() {
String[] arrayOfWords = {"Goodbye", "World"};
List<String> words = Arrays.asList(arrayOfWords);
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
}
In the example above, split() gets String [] instead of String, so the arrays are not mapped to a stream separately, but to the content of the stream. All the individual streams generated using map(Arrays::stream) are merged into one stream.
matching
Matching is relatively simple, returning a boolean
- anyMatch(): Match at least one
- allMatch(): All matches
- noneMatch(): All mismatches, as opposed to allMatch
@Test
public void anyMatch() {
List<Dish> menu = init();
Assert.assertEquals(true, menu.stream().anyMatch(Dish::isVegetarian));
}
@Test
public void allMatch() {
List<Dish> menu = init();
Assert.assertEquals(true, menu.stream().allMatch(d -> d.getCalories() < 1000));
}
@Test
public void noneMatch() {
List<Dish> menu = init();
Assert.assertEquals(true, menu.stream().noneMatch(d -> d.getCalories() >= 1000));
}
lookup
There are two methods to find: findFirst() and findAny(), returning an Optional < T > collection.
If you don't care which element is returned, use findAny(), because it has fewer restrictions when using parallel streams.
@Test
public void findFirst() {
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst(); // 9
System.out.println(firstSquareDivisibleByThree.get());
}
@Test
public void findAny() {
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findAny(); // 9
System.out.println(firstSquareDivisibleByThree.get());
}
reduction
Reduction is used when summarizing all the data in the aggregation. Let's say max, min, sum.
@Test
public void reduce() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum);
}
Primitive type flow specialization
In the process of internal iteration, basic types are automatically packed and unpacked. Java 8 provides IntStream, DoubleStream and LongStream to avoid unwanted boxing and unpacking.
- General flow specialization flow: mapToInt(), mapToLong(), mapToDouble()
- Specialized Conversion Common Flow: boxed()
public void boxedStream() {
List<Dish> menu = init();
// Specialization
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
// Return to ordinary Stream
Stream<Integer> stream = intStream.boxed();
}
Java 8 introduces two static methods that can be used for IntStream and LongStream to generate a given range of digital streams:
- range(min, max): Random generated numbers do not contain max, that is (min, max)
- Range Closed (min, max): Random generated numbers contain max, i.e. (min, max)