I thought the new Java8 feature would take 3 articles to write, but I found that the Stream interface is not as many as I expected. I can finish it today.

Flow at the beginning of experience

First of all, define a dish category:

  • Dish
public class Dish {
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;
    
    public boolean isVegetarian(a) {
        return vegetarian;
    }
    
    // omit the set, get, toString methods
}
Copy the code

Then create a static method and set it as a member variable of the class for testing.

public static List<Dish> getDishes(a) {
    return Arrays.asList(
        new Dish("pork".false.800, Dish.Type.MEAT),
        new Dish("beef".false.700, Dish.Type.MEAT),
        new Dish("chicken".false.400, Dish.Type.MEAT),
        new Dish("french fries".true.530, Dish.Type.OTHER),
        new Dish("rice".true.350, Dish.Type.OTHER),
        new Dish("pizza".true.550, Dish.Type.OTHER),
        new Dish("prawns".false.300, Dish.Type.FISH),
        new Dish("salmon".false.450, Dish.Type.FISH)
    );
}
Copy the code

Okay, now we have a requirement, find all the items in the menu that are less than 400 calories, and rank them by number of calories.

Before java8, and even after java8, some people thought about using an intermediate variable to ensure that the dishes fit the requirements and then sort them.

public static List<String> beforeJava8(a) {
    List<Dish> lowCaloricDishes = new ArrayList<>();
    for (Dish dish : dishes) {
        if (dish.getCalories() < 400) {
            lowCaloricDishes.add(dish);
        }
    }

    lowCaloricDishes.sort(Comparator.comparingInt(Dish::getCalories));
// lowCaloricDishes.sort((d1, d2) -> Integer.compare(d1.getCalories(), d2.getCalories()));
    List<String> res = new ArrayList<>();

    for (Dish dish : lowCaloricDishes) {
        res.add(dish.getName());
    }
    return res;
}
Copy the code

Since I covered method references in the previous article, I’ll use them directly here, but the following line also has regular Lambda expressions written.

LowCaloricDishes is only used once, and it’s really only a temporary variable. How about you skip the process of creating variables, you just give me the data, and I filter and sort it and get what I want, just like an assembly line.

public static List<String> afterJava8(a) {
    return dishes.stream()
        .filter(dish -> dish.getCalories() < 400)
        .sorted(Comparator.comparing(Dish::getCalories))
        .map(Dish::getName)
        .collect(Collectors.toList());
}
Copy the code

That’s a Stream

The definition of the flow

A sequence of elements generated from a source that supports data processing operations

A stream is similar to a collection in that a collection is a data structure whose primary purpose is to store and access elements, whereas a stream’s primary purpose is to perform a series of operations on elements.

In layman’s terms, a collection is your movie download, and a stream is your online viewing. You just need to think of streams as high-level collections. Streams have two important characteristics:

  • Pipeline: Many streams themselves return a stream so that multiple streams can be linked together like a pipeline.
  • Internal iteration:Inner iteration is to encapsulate the iteration, such ascollect(Collectors.toList)And the corresponding external iteration isfor-each

It is worth noting that, like iterators, the stream can only be traversed once, and then the stream can be said to be consumed.

The construction of a flow

The construction of a flow

There are four common ways streams can be built, either with static methods of the Stream class or with static methods of someone else’s class.

  • Create a flow from a value
  • Create streams from arrays
  • A stream is generated from a file
  • A stream is generated by a function
public static void buildStream(a) throws IOException {
    Stream<String> byValue = Stream.of("java8"."c++"."go");

    Stream<Object> empty = Stream.empty();

    int[] nums = {1.2.3.4.5};
    IntStream byInts = Arrays.stream(nums);

    Stream<String> byFiles = Files.lines(Paths.get(""));

    Stream<Integer> byFunction1 = Stream.iterate(0, n -> n * 2);
    Stream<Double> byFunction2 = Stream.generate(Math::random);

    Stream<String> java = Stream.of("java");
}
Copy the code

The operation of the flow

Stream operations that can be joined together are called intermediate operations, and operations that close streams are called terminal operations

In layman’s terms, an operation that returns a stream is called an intermediate operation, and an operation that puts back something other than a stream is called a terminal operation.

You can find out which java8 interfaces are intermediate and which are terminal. Because those interface description is too official, estimate I posted also no one will look carefully, so want to see directly go to the official.

The use of the flow

I will tell the story in the order of Java API on the official website. In a word, I did not learn the stream before mainly because I felt there would be a lot of interfaces. How could I possibly remember so many?

Start by building a list of numbers:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 5, 6, 7);

In the middle of operation

The intermediate operation has to redo, filter, truncate, view, skip, sort, these believe everyone can understand what is meant.

public static void midOperation(a) {
    numbers.stream()
        .distinct()
        .forEach(System.out::println);

    List<Integer> filter = numbers.stream()
        .filter(n -> n % 2= =0)
        .collect(Collectors.toList());

    numbers.stream()
        .limit(3)
        .forEach(System.out::println);

    numbers.stream()
        .peek(integer -> System.out.println("consume operation:" + integer))
        .forEach(System.out::println);

    List<Integer> skip = numbers.stream()
        .skip(2)
        .collect(Collectors.toList());

    numbers.stream()
        .sorted()
        .forEach(System.out::println);
}
Copy the code

Mapping of intermediate operations

Map and flatMap. Note that the map here is not the same as the hashmap, but rather refers to what is mapped or converted into what.

public static void midOperation(a) {
	List<String> map = numbers.stream()
                .map(Object::toString)			//这里就是把int映射成了string
                .collect(Collectors.toList());
}
Copy the code

For flat mapping, we now have a requirement that we have a List of words, such as {“hello”, “world”}, and return different characters in it, that is, List

.

It’s not easy. You map the word into letters and then duplicate it.

public static void flatMapDemoNoral(a) {
    List<String> words = Arrays.asList("hello"."world");
    List<String[]> normal = words.stream()
        .map(str -> str.split(""))
        .distinct()
        .collect(Collectors.toList());
}
Copy the code

This does work, but note that the mapping is split(), which returns String[], so the whole thing returns List

.
[]>

Then I map each String[] array to a stream

public static void flatMapDemoMap(a) {
    List<String> words = Arrays.asList("hello"."world");
    List<Stream<String>> usingMap = words.stream()
                .map(str -> str.split(""))
                .map(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
}
Copy the code

List

> is returned.

FlatMap is designed to address this situation

public static void flatMapDemoFlatMap(a) {
    List<String> words = Arrays.asList("hello"."world");
    List<String> usingFlatMap = words.stream()
                .map(str -> str.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
}
Copy the code

A map maps each element into a separate stream, while a flattened map saves the elements and maps them into a stream.

Find and match

In addition to collect() and forEach() commonly used in writing examples above, terminal operations also have two major directions of lookup and specification.

Because there is nothing to say, just go to the code:

public static void endOperationFindAndMatch(a) {
    if (dishes.stream().noneMatch(Dish::isVegetarian)) {
        System.out.println("All dishes are non-vegetarian.");
    }
    if (dishes.stream().allMatch(Dish::isVegetarian)) {
        System.out.println("All the dishes are vegetarian.");
    }
    if (dishes.stream().anyMatch(Dish::isVegetarian)) {
        System.out.println("At least one of the dishes is vegetarian.");
    }

    Optional<Dish> any = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .findAny();
    Optional<Dish> first = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .findFirst();
}
Copy the code

Reduction (calculation)

For the protocol operation of convection, there are general operations that can directly call the interface, and there is also the help of reduce().

For common operations, there are interfaces for things like summation, Max, min.

public static void endOperationCalculate(a) {
    long count = dishes.stream()
        .filter(meal -> meal.getCalories() <= 1000)
        .count();
    Optional<Dish> max = dishes.stream()
        .max(Comparator.comparingInt(Dish::getCalories));
    Optional<Dish> min = dishes.stream()
        .min(Comparator.comparing(Dish::getName));

}
Copy the code

But if you want to sum elements, you use reduce()

It is generally used to take two arguments, an initial value and a BinaryOprator

, to combine the two elements to produce a new value.

public static void reduceDemo(a) {
    List<Integer> numbers = Arrays.asList(1.2.3.4.5.5.5.5.6.7);
    Integer sum = numbers.stream().reduce(0, Integer::sum);
    
    // It's also easier to multiply all the elements
    Integer multi = numbers.stream().reduce(0, (a, b) -> a * b);
    
    // Get the maximum value
    Optional<Integer> max = numbers.stream().reduce(Integer::max);
}
Copy the code

Optional class

It keeps returning Optional

, which is a container class that indicates that a value exists or does not exist. For example, findAny() at the beginning might not be able to find the item. Java8 was introduced mainly to avoid returning problematic null.

Just a few of the more commonly used apis, as for the rest of the official API can be read online, said enough API today

  • isPresent()Will be inOptionalReturns true if a value is included, false otherwise
  • ifPresent(Consumer<T> block)The given code block is executed when the value is present
  • get()Returns a value if it exists,Otherwise throwNoSuchElementabnormal
  • orElse()Returns a value if it exists, a default value otherwise
public static void optionalDemo(a) {
    //ifPresent
    dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny()
        .ifPresent(dish -> System.out.println(dish.getName()));

    //isPresent
    boolean isLowCalories= dishes.stream()
        .filter(dish -> dish.getCalories() <= 1000)
        .findAny()
        .isPresent();
	
    //get
    Optional<Dish> optional = dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny();
    if (optional.isPresent()) {
        Dish dish = optional.get();
    }

    //orElse
    Dish dishNormal = dishes.stream()
        .filter(Dish::isVegetarian)
        .findAny()
        .orElse(new Dish("java".false.10000, Dish.Type.OTHER));
}
Copy the code

conclusion

Again, there are a few minor points left out, such as the parallel flow section, the collector section, but these are relatively in-depth. Two more articles later, you’ve got as comprehensive a look at the new Java8 features as possible. After digestion, you can go to see “Java8 actual combat”, reading is the focus of learning new knowledge, and finally is the application.