Stream API for Java 8 Learning Notes

Keywords: Java Lambda less

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

  1. 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().
  2. Create by value: Stream < String > stream = Stream. of ("Java 8", "Lambdas", "In", "Action");
  3. Created by an array:
        int[] numbers = {2, 3, 5, 7, 11, 13};
        int sum = Arrays.stream(numbers).sum();
  1. 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) {
        }
  1. 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)

Posted by noisyscanner on Fri, 05 Jul 2019 17:47:43 -0700