что транслируется
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())));