Потоковая работа новых функций Java 8

Java

что транслируется

API Java 8 добавляет новую абстракцию под названием Stream, которая позволяет обрабатывать данные декларативным способом.

Потоки обеспечивают высокоуровневую абстракцию над операциями над наборами и представлениями Java интуитивно понятным способом, аналогичным запросу данных из базы данных с помощью операторов SQL.

Stream API может значительно повысить производительность Java-программистов, позволяя программистам писать эффективный, чистый и лаконичный код.

Этот стиль рассматривает набор элементов, подлежащих обработке, как своего рода поток, поток передается в конвейере и может обрабатываться на узлах конвейера, например фильтрация, сортировка, агрегация и т. д.

Поток элементов обрабатывается промежуточными операциями в конвейере, и, наконец, результат предыдущей обработки получается терминальной операцией.

1. Пример потоковой передачи

1.1 Создать класс сущностей

public class Person {

    private String name;
    private Integer age;
    private Integer score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }

    public Person() {
    }

    public Person(String name, Integer age, Integer score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

1.2 Традиционный метод инициализации объекта

public class Program {
    public static void main(String[] args) {
        //使用构造器设置对象信息
//        Person xiaomign = new Person("小明", 28, 90);

        //使用getter、setter方式设置对象信息
        Person xiaoming = new Person();
        xiaoming.setName("小明");
        xiaoming.setAge(18);
        xiaoming.setScore(90);
    }
}

1.3 Инициализация объектов с помощью потоковых операций

1.3.1 Изменить класс объекта

public class Person {

    private String name;
    private Integer age;
    private Integer score;

    public String getName() {
        return name;
    }

    public Person setName(String name) {
        this.name = name;
        return this;
    }

    public Integer getAge() {
        return age;
    }

    public Person setAge(Integer age) {
        this.age = age;
        return this;
    }

    public Integer getScore() {
        return score;
    }

    public Person setScore(Integer score) {
        this.score = score;
        return this;
    }

    public Person() {
    }

    public Person(String name, Integer age, Integer score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

1.3.2 Использование потоковых операций

//流式操作
xiaoming.setName("小明").setAge(20).setScore(100);

2. Потоковые операции над коллекциями

Потоковая операция коллекции — это новая функция Java 8. Потоковая операция не является структурой данных и не отвечает за какое-либо хранение данных. Это больше похоже на итератор, который может упорядоченно получать все данные в источнике данных. , и может что-то сделать с этими данными.Возвращаемое значение каждого метода потоковой операции — это сам поток..

2.1 Три этапа потоковой передачи

2.1.1 Получение источников данных: коллекции, массивы

  • установить источник данных

    public class Data {
    
        /**
         * 数据源
         */
        public static ArrayList<Person> getData() {
            ArrayList<Person> list = new ArrayList<Person>();
    
            list.add(new Person("小明", 18, 100));
            list.add(new Person("小丽", 19, 70));
            list.add(new Person("小王", 22, 85));
            list.add(new Person("小张", 20, 90));
            list.add(new Person("小黑", 21, 95));
            return list;
        }
    }
    
  • Как получить источник данных

    public class Program {
        public static void main(String[] args) {
    
            // 获取数据源方式1
            Stream stream = Data.getData().stream();
    
            // 获取数据源方式2
            Stream.of(Data.getData());
            
            // 获取数据源方式3
            	//数据源为数组
        }
    }
    

2.1.2 Процесс обработки данных: фильтрация, сортировка, отображение и т. д. (промежуточные операции)

Промежуточная операция 1: фильтр

  • Используйте настраиваемые критерии фильтра для фильтрации данных

    // 中间操作1: filter
    // filter是一个过滤器,可以自定义一个过滤条件,将流中满足条件的元素保留
    // 查找集合中成绩小于80的学生
    List<Person> list = Data.getData().stream()
        .filter(ele -> ele.getScore() < 80)
        .collect(Collectors.toList());
    System.out.println(list);
    

Промежуточная операция 2: различная

  • Используйте отдельные для реализации операций дедупликации

    Добавить повторяющиеся данные в источник данных

    list.add(new Person("小黑", 21, 95));	//此时list中有两个小黑
    

    Переопределить методы hashCode() и equals() в классе сущностей.

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
            Objects.equals(age, person.age) &&
            Objects.equals(score, person.score);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age, score);
    }
    

    Правила дедупликации:

    • Сначала оцените hashCode() объекта

    • Если hashCode() тот же, то оценивайте equals()

    // 中间操作2: distinct
    // distinct: 取出集合中不同的元素
    // 去重规则:
    // 1.先判断对象的hashCode()
    // 2.如果hashCode()相同再判断equals()
    Data.getData().stream().distinct().forEach(System.out::println);
    

Уведомление: Если данные Xiao Hei совпадают, но их нужно сохранить в двух копиях, вы можетеhashCode()В методе возвращается случайное число, и случайное число будет таким же с небольшой вероятностью.Для обеспечения стабильности можно использоватьequals()метод возвращаетfalse, который сохраняет два маленьких черных с одинаковой информацией.

Промежуточная операция 3: сортировка

  • использоватьsorted()метод сортировки по оценкам в порядке возрастания

    Требовать, чтобы класс сущности реализовывал интерфейс Comparable и переопределял метод

    // 中间操作3: sorted
    // sorted: 对返回的元素进行排序
    // sorted(): 要求实体类实现Comparable接口并重写方法
    Data.getData().stream().sorted().forEach(System.out::println);
    

Промежуточная операция 4: ограничение

  • Возьмите первые три данных в источнике данных

    // 中间操作4: limit
    // limit: 限制,只取流中前指定位的数据
    // 在数据源中取前三个数据
    Data.getData().stream().limit(3).forEach(System.out::println);
    

Промежуточная операция 5: пропустить

  • Пропустите первые три элемента и возьмите оставшиеся элементы

    // 中间操作5: skip
    // skip: 跳过
    // 跳过前三个元素,取后面剩下的元素
    Data.getData().stream().skip(3).forEach(System.out::println);
    

Промежуточная операция 6: карта

Сопоставление элементов, которое заменяет элементы в потоке указанными элементами.

  • Используйте карту, чтобы заменить объект именем объекта

    // 中间操作6: map
    // map: 元素映射,用指定的元素替换掉流中的元素
    // 将流中的Person对象替换位他们的姓名
    Data.getData().stream().map(ele -> ele.getName()).forEach(System.out::println);
    

2.1.3 Интегрировать данные в потоки: превращать в наборы, количества (завершающие операции)

Заключительная операция 1: сбор

  • Преобразовать в список

    public class Program {
        public static void main(String[] args) {
    
            // 获取数据源方式1
            Stream<Person> stream = Data.getData().stream();
    
            // 最终操作1: collect,配合Collectors使用
            // 将集合中的元素转换成List
            List<Person> list = stream.collect(Collectors.toList());
    
            System.out.println(list);
        }
    }
    

    результат операции

  • конвертировать в набор

    // 将集合中的元素转换为Set
    Set<Person> set = stream.collect(Collectors.toSet());
    System.out.println(set);
    

    результат операции

  • преобразовать в карту

    // 转换为Map(name为键,score为值)
            // 方式1
    //        Map<String, Integer> map = stream.collect(Collectors.toMap(
    //                ele -> ele.getName(),
    //                ele -> ele.getScore()
    //        ));  
            
            // 方式2        
            Map<String, Integer> map = stream.collect(Collectors.toMap(
                    Person::getName,
                    Person::getScore
            ));
    

    результат операции

Заключительная операция 2: уменьшить

Идея уменьшить

Например, при вычислении суммы элементов массива сначала будет вычислена сумма первых двух чисел, а затем сумма первых двух чисел будет суммирована с третьим числом. сумма трех чисел будет добавлена ​​к Добавить четвертые числа и так далее.

  • Вычислить сумму данных в массиве

    // 最终操作2: reduce(将数据汇总在一起)
    Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Optional<Integer> res = stream1.reduce((n1, n2) -> n1 + n2);
    // 获取到最终的返回值
    System.out.println(res.get());
    

  • Используйте сокращение для вычисления суммы оценок в объекте Person.

    // 计算Person中Score的和
    Optional<Person> res = stream.reduce(
        (n1, n2) -> new Person().setScore(n1.getScore() + n2.getScore())
    );
    System.out.println(res.get().getScore());
    

Недостатки: описанный выше метод записи будет каждый раз генерировать временный объект, что приведет к ненужной потере производительности.

  • Используйте сокращение для вычисления суммы оценок в объекте Person (оптимизировано)

    // 计算Person中Score的和(使用临时变量,减少性能开销)
    Person temp = new Person();
    Optional<Person> res = stream.reduce(
        (n1, n2) -> temp.setScore(n1.getScore() + n2.getScore())
    );
    System.out.println(res.get().getScore());
    

Заключительная операция 3: макс и мин

  • Используйте max, чтобы найти человека с самой высокой оценкой в ​​​​Person.

    // 最终操作3: max和min
    // 需求1: 找到集合中成绩最高的人的信息
    Person max = stream.max(
        (ele1, ele2) -> ele1.getScore() - ele2.getScore()
    ).get();
    System.out.println(max);
    

  • Используйте min, чтобы найти человека с самой низкой оценкой в ​​​​Person.

    // 需求2: 找到集合中成绩最低的人的信息
    Person min = stream.min(
        (ele1, ele2) -> ele1.getScore() - ele2.getScore()
    ).get();
    System.out.println(min);
    

Заключительное действие 4: сопоставление

  • Используйте anyMatch, чтобы узнать, есть ли у кого-либо в коллекции оценка выше 80.

    // 判断集合中是否包含成绩大于80的学员
    boolean res1 = stream.anyMatch((ele) -> ele.getScore() > 80);
    System.out.println(res1);
    

  • Используйте allMatch, чтобы увидеть, все ли оценки в наборе выше 60.

    //查看集合中的人的成绩是否全部高于60
    boolean res2 = stream.allMatch((ele) -> ele.getScore() > 60);
    System.out.println(res2);
    

  • Используйте noneMatch, чтобы увидеть, не содержит ли счет людей в наборе ничего меньше 80.

    boolean res3 = stream.noneMatch((ele) -> ele.getScore() < 80);
    System.out.println(res3);
    

Заключительная операция 5: счет

  • Используйте count для подсчета количества фрагментов данных в метаданных.

    // 最终操作5: 求元数据中有多少个元素
    long count = stream.count();
    System.out.println(count);
    

Заключительное действие 6: forEach

  • Перебор элементов в коллекции с помощью forEach

    // 最终操作6: forEach
    // stream.forEach(ele -> System.out.println(ele));
    stream.forEach(System.out::println);
    

Заключительное действие 7: findFirst и findAny

  • FindFirst: получить первый элемент в потоке FindAny: получить любой элемент в потоке (не случайным образом) Для последовательных потоков результат эквивалентен findFirst. findAny, используемый в параллельных потоках, может совпадать или не совпадать с findFirst.
// FindFirst: 获取流中的第一个元素
// FindAny: 获取流中任意一个元素(并不是随机获取元素)
//          对于串行流,结果等同于findFirst
//          findAny用于并行流中可能会与findFirst一样,也可能不一样
System.out.println(Data.getData().parallelStream().findFirst());
System.out.println(Data.getData().stream().findFirst());
System.out.println(Data.getData().parallelStream().findAny());
System.out.println(Data.getData().stream().findAny());

Заключительные действия.

  • Почему она называется завершающей операцией?

    Person max = stream.max(
        (ele1, ele2) -> ele1.getScore() - ele2.getScore()
    ).get();
    Person min = stream.min(
        (ele1, ele2) -> ele1.getScore() - ele2.getScore()
    ).get();
    

Представление сообщения об ошибкеПоток обрабатывается или был закрыт, если она была закрыта, то, конечно, сообщит об ошибке при повторном вызове, поэтому она и называется завершающей операцией.

3. Параллельные потоки

3.1 Как получить параллельный поток

// 并行流
// 获取并行流的两种方式
Data.getData().stream().parallel();
Data.getData().parallelStream();

3.2 Сравнение параллельных потоков и последовательных потоков

// 串行流: 19920ms
// 并行流: 12204ms
long startTime = System.currentTimeMillis();
//LongStream.rangeClosed(0L, 50000000000L)
//    .reduce(Long::sum);
LongStream.rangeClosed(0L, 50000000000L)
    .parallel()
    .reduce(Long::sum);
long endTime = System.currentTimeMillis();

System.out.println(endTime - startTime);

3.3 flatMap

String[] array = {"hello", "world"};
// 需要获取所有字符 List -> h, e, l, l, o, w, o, r, l, d
//        Arrays.stream(array)
//                .map(ele -> ele.split(""))
//                .forEach(ele -> System.out.println(ele.length));
System.out.println(Arrays.stream(array)
                   .map(ele -> ele.split(""))
                   .flatMap(Arrays::stream)
                   .collect(Collectors.toList()));

4.Collectors

Collectors — это класс инструментов, который предоставляет несколько методов и возвращает объект класса реализации интерфейса Collector.

4.1 maxBy

Получить самый большой элемент в потоке по заданному правилу

System.out.println(Data.getData().stream()
                .collect(Collectors.maxBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));

4.2 minBy

Получить наименьший элемент в потоке по заданному правилу

System.out.println(Data.getData().stream()
                .collect(Collectors.minBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));

4.3 joining

Слияние, объединение элементов в потоке в виде строк

// 把Person中的姓名拼成一个字符串
String res1 = Data.getData().stream()
    .map(Person::getName)
    .collect(Collectors.joining());
System.out.println(res1);

String res2 = Data.getData().stream()
    .map(Person::getName)
    .collect(Collectors.joining("-"));
System.out.println(res2);

String res3 = Data.getData().stream()
    .map(Person::getName)
    .collect(Collectors.joining("-", "{", "}"));
System.out.println(res3);

4.4 summingInt

Вычислить сумму типа int, сопоставить элементы потока с элементами типа int и sum

  • Суммируйте оценки объектов Person

    // 将Person对象的成绩进行求和
    System.out.println(Data.getData().stream()
                       .collect(Collectors.summingInt(ele -> ele.getScore())));
    

4.5 averagingInt

Вычислить среднее значение типа int

  • Рассчитайте средний балл для неуспевающих учащихся

    System.out.println(Data.getData().stream()
                       .filter(ele -> ele.getScore() < 60)
                       .collect(Collectors.averagingInt(Person::getScore)));
    

4.6 summarizingInt

Сопоставьте элементы в потоке с элементами типа int и получите информацию описания этих данных.

System.out.println(Data.getData().stream()
                   .collect(Collectors.summarizingInt(ele -> ele.getScore())));