Stream API是Java 8引入的一项重要特性,它提供了一种新的处理集合数据的方式。Stream可以看作是一种高级的迭代器,它允许以声明式的方式对集合进行各种操作,如过滤、映射、排序、归约等。它可以简化集合处理的代码,并且在处理大数据集时具有性能优势。
个人理解:stream流就像一条源源不断的流水线或者管道,原材料从一端进入生产线,经过一系列的加工和处理,最终成品从另一端输出。在这个过程中,每个工人(方法)负责自己的任务,并将结果传递给下一个工人(方法),形成一个连续的操作链。直到遇到终止方法,才会生成最后的结果。
1、stream(): 返回一个顺序流(Stream)。parallelStream(): 返回一个并行流(Stream)。
并行处理:Stream API提供了并行处理的能力,可以利用多线程来加速大数据集的处理。通过调用parallelStream()方法,可以将流转换为并行流,使得操作可以并行地执行。
List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
2、filter(Predicate predicate): 过滤元素,保留符合条件的元素。传入是一个Predicate函数式接口,返回true:保留,false: 丢弃
List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> filteredStream = list.stream().filter(s -> s.startsWith("a"));
3、distinct(): 去除重复的元素。
List<String> list = Arrays.asList("apple", "banana", "apple");
Stream<String> distinctStream = list.stream().distinct();
4、sorted(): 对元素进行排序,默认使用自然顺序。
List<Integer> list = Arrays.asList(3, 1, 2);
Stream<Integer> sortedStream = list.stream().sorted();
4.1 也可以自定义比较器,也就是参数中需要填写一个比较器 “sorted(Comparator super T> comparator)” 例如:
//字符串忽略大小写并排序
List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
List<String> sortedNames = names.stream().sorted((a, b) -> a.compareToIgnoreCase(b)).collect(Collectors.toList());
//对引用类型排序,通过其中的一个属性排序
class Person {
private String name;
private int age;
// 省略构造函数和其他方法
// Getter和Setter方法
// ...
}
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 20)
);
List<Person> sortedPersons = persons.stream()
.sorted(Comparator.comparingInt(Person::getAge))
.collect(Collectors.toList());
5、limit(long maxSize): 限制流中元素的数量。
List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> limitedStream = list.stream().limit(2);
6、forEach(Consumer action): 对每个元素执行给定的操作。就是遍历
List<String> list = Arrays.asList("apple", "banana", "cat");
list.stream().forEach(System.out::println);
7、map(Function
List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<Integer> lengthStream = list.stream().map(String::length);
7.1 map()方法允许我们在流中对每个元素进行自定义的转换操作,并生成一个新的流。通过传递适当的Function函数接口实现,我们可以实现各种转换操作,如类型转换、属性提取、计算转换等。例如:
class Person {
private String name;
// ...
}
List<Person> persons = Arrays.asList(
new Person("Alice"),
new Person("Bob"),
new Person("Charlie")
);
//提取name,生成一个新的List
List<String> names = persons.stream()
.map(Person::getName)
.collect(Collectors.toList());
//将一个person转换为一个student
List<Student> students = persons.stream()
.map(person -> {
String name = person.getName();
int age = person.getAge();
String school = getSchoolBasedOnAge(age); // 自定义的逻辑方法
return new Student(name, age, school);
})
.collect(Collectors.toList());
private static String getSchoolBasedOnAge(int age) {
if (age < 18) {
return "High School";
} else {
return "University";
}
}
8、collect(Collector super T, A, R> collector): 将流中的元素收集到一个集合或其他可变结果容器中。最简单的就是返回一个新的List,上面方法都是
List<String> list = Arrays.asList("apple", "banana", "cat");
List<String> collectedList = list.stream().collect(Collectors.toList());
8.1、collect(Collector super T, A, R> collector) 是 Stream API 中的一个终端操作方法,用于将流中的元素收集到一个集合或其他可变结果容器中。该方法接受一个Collector对象作为参数,用于定义收集元素的规则。Collector对象可以通过Collectors工厂类的静态方法创建,也可以自定义实现。
Collector super T, A, R> 中的泛型参数表示以下内容:
T:表示流中的元素类型。? super T 表示任何 T 类型的超类型,这允许 Collector 处理 T 类型及其子类型的元素。
A:表示用于累积流元素的可变结果容器(accumulator)的类型。在收集过程中,Collector 将逐个元素累积到 A 类型的容器中。
R:表示收集操作的最终结果类型。Collector 最终将累积的结果转换为 R 类型,并返回结果。
在 Collector super T, A, R> 中,? super T 允许处理 T 类型及其子类型的元素。这种设计使得 Collector 更加通用,可以处理不同类型的元素,而不仅仅局限于 T 类型。累积结果容器的类型 A,以及收集操作的最终结果类型 R
例如:常用的list转map
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
Map<String, Integer> nameToAgeMap = persons.stream()
.collect(Collectors.toMap(Person::getName, Person::getAge));
System.out.println(nameToAgeMap);
}
转为< id:实体 >
//在toMap()方法中,我们传递了三个参数:Person::getName用于提取姓名作为键,Function.identity()
//用于将Person对象本身作为值,以及一个合并函数 (existingValue, newValue) -> newValue 来处理
//重复的键。在合并函数中,我们选择使用新值作为合并后的值。
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Alice", 35)
);
Map<String, Person> nameToPersonMap = persons.stream()
.collect(Collectors.toMap(Person::getName, Function.identity(), (existingValue, newValue) -> newValue));
System.out.println(nameToPersonMap);
}
8.2 使用collect()方法分组,以年龄分组
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("Dave", 25)
);
Map<Integer, List<Person>> ageGroupMap = persons.stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println(ageGroupMap);
}
多层分组,根据城市和年龄进行多级分组。
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", "New York", 25),
new Person("Bob", "London", 30),
new Person("Charlie", "New York", 35),
new Person("Dave", "London", 25)
);
Map<String, Map<String, List<Person>>> cityToAgeGroupMap = persons.stream()
.collect(Collectors.groupingBy(Person::getCity,
Collectors.groupingBy(person -> person.getAge() < 30 ? "Young" : "Old")));
System.out.println(cityToAgeGroupMap);
得到了一个名为resultMap的嵌套的Map,其中外层的键是姓名,内层的键是年龄,值是对应的Person对象。
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("Dave", 25)
);
Map<String, Map<Integer, Person>> resultMap = persons.stream()
.collect(Collectors.groupingBy(
Person::getName,
Collectors.toMap(
Person::getAge,
person -> person
)
));
System.out.println(resultMap);
}
还可以做这些事情:
统计和汇总:
Collectors.counting(): 统计流中元素的数量。
Collectors.summingInt(toIntFunction): 对流中的元素进行整数求和。
Collectors.averagingInt(toIntFunction): 计算流中元素的平均值。
Collectors.summarizingInt(toIntFunction): 生成流中元素的汇总统计结果,包括数量、总和、平均值、最大值和最小值。
字符串拼接和连接:
Collectors.joining(): 将流中的元素按照指定的分隔符连接成一个字符串。
Collectors.joining(delimiter, prefix, suffix): 将流中的元素按照指定的分隔符、前缀和后缀连接成一个字符串。
9、allMatch(Predicate super T> predicate): 检查流中的所有元素是否都满足给定条件。返回boolean
List<Integer> list = Arrays.asList(2, 4, 6);
boolean allEven = list.stream().allMatch(n -> n % 2 == 0);
10、skip(long n): 跳过流中的前n个元素。这个可以用于列表分页
代码如下:跳过当前页前面的元素,然后再通过limit限制每页的大小
11、reduce(BinaryOperator accumulator): 根据给定的累加器函数对流中的元素进行归约操作。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = list.stream().reduce(0, (a, b) -> a + b);
找一个列表中年龄最大的Person
List<Person> people = Arrays.asList(
new Person("John", 25),
new Person("Alice", 30),
new Person("Bob", 20)
);
Person oldestPerson = people.stream().reduce((p1, p2) -> p1.getAge() > p2.getAge() ? p1 : p2).orElse(null);
当然还可以做其他聚合和累积操作,具体在遇到聚合或者累加时,可以想到.reduce方法。
12、distinct(): 去除重复的元素。
List<String> list = Arrays.asList("apple", "banana", "apple");
Stream<String> distinctStream = list.stream().distinct();
13、flatMap(Function super T, ? extends Stream extends R>> mapper): 将流中的每个元素进行映射后扁平化成一个流。这个也是很好用的方法
List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
List<Integer> flattenedList = nestedList.stream().flatMap(Collection::stream).collect(Collectors.toList());
13.1、flatMap() 方法在处理嵌套结构、处理多个集合或流之间的映射关系时非常有用。它可以将嵌套的集合或流展开为一个更简单的结构,方便后续的处理和操作。
List<List<List<String>>> nestedList = Arrays.asList(
Arrays.asList(
Arrays.asList("apple", "banana"),
Arrays.asList("cherry", "date")
),
Arrays.asList(
Arrays.asList("grape", "kiwi"),
Arrays.asList("lemon", "mango")
)
);
List<String> flattenedList = nestedList.stream()
.flatMap(List::stream)
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flattenedList); // 输出:[apple, banana, cherry, date, grape, kiwi, lemon, mango]
假设有一个 Person 类型,其中每个人可以有多个爱好(hobbies)。每个爱好又可以有多个标签(tags)。我们可以使用 flatMap() 方法来处理这种嵌套的引用类型结构。
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("John", Arrays.asList("Reading", "Painting")),
new Person("Alice", Arrays.asList("Music", "Cooking", "Hiking")),
new Person("Bob", Arrays.asList("Photography"))
);
List<String> allHobbies = people.stream()
.flatMap(person -> person.getHobbies().stream())
.collect(Collectors.toList());
System.out.println(allHobbies);
}
使用 flatMap() 方法,我们将 people 列表中每个人的爱好(List)流化,并将它们扁平化成一个单一的流。最后,我们将流收集为一个列表 allHobbies,其中包含所有人的爱好。
后续有更好用的再补充。