Java 8 Stream相关知识点的归纳总结-重点groupby总结
Java 8 Stream是处理集合数据的新方式。它提供了一种更高效、更简洁的方法来处理数据,使用 Stream API 你可以完成很多原先需要写循环才能解决的问题。
Stream提供了很多操作方法,包括中间操作和终止操作。中间操作返回一个新的Stream对象,你可以进行进一步的操作;终止操作不再返回Stream对象,它们可能返回一个结果,也可能不返回任何结果。
一些常用的中间操作方法包括 filter、map、flatMap、distinct、sorted、peek、limit、skip。一些常用的终止操作方法包括 forEach、forEachOrdered、count、collect、min、max、reduce、anyMatch、allMatch、noneMatch、findFirst、findAny。
在对Stream进行操作时,需要注意以下几点:
- 操作不会改变原始的集合元素。
- 操作通常是延迟执行的。
- 操作可以按需执行,不必要全部执行。
- 操作时可以并行化操作的,提高处理效率。
下面是一些常见场景下的Stream用法:
- 集合元素遍历
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
list.stream().forEach(System.out::println);
Copy
Java
- 过滤操作
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
List<String> result = list.stream().filter(s -> s.startsWith("A")).collect(Collectors.toList());
Copy
Java
- 转换操作
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
List<Integer> result = list.stream().map(String::length).collect(Collectors.toList());
Copy
Java
- 合并操作
List<String> list1 = Arrays.asList("A", "B", "C");
List<String> list2 = Arrays.asList("D", "E", "F");
List<String> result = Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList());
Copy
Java
- 统计操作
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics statistics = list.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println(statistics.getMax());
System.out.println(statistics.getMin());
System.out.println(statistics.getSum());
System.out.println(statistics.getAverage());
Copy
Java
- 分组操作
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40),
new Person("Eddie", 45)
);
Map<Integer, List<Person>> result = persons.stream().collect(Collectors.groupingBy(Person::getAge));
Copy
Java
- 去重操作
List<Integer> list = Arrays.asList(1, 2, 3, 3, 4, 5, 5, 5);
List<Integer> result = list.stream().distinct().collect(Collectors.toList());
Copy
Java
- 拆分操作
List<String> list = Arrays.asList("Apple,Banana", "Orange,Mango", "Grape,Peach");
List<String> result = list.stream().flatMap(s -> Arrays.stream(s.split(","))).collect(Collectors.toList());
Copy
Java
- 排序操作
List<Integer> list = Arrays.asList(2, 4, 1, 3, 5);
List<Integer> result1 = list.stream().sorted().collect(Collectors.toList());
List<Integer> result2 = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
List<Integer> result3 = list.stream().sorted((o1, o2) -> o2.compareTo(o1)).collect(Collectors.toList());
Copy
Java
这些只是Stream API的一些常见用法,理解了这些基本用法之后,可以根据实际的需求进行更丰富的操作。Stream API 的出现,使得Java集合操作更加简捷、高效,也拓宽了Java 8的应用场景。
对象中某个键值对应
如果你想将 List<Person>
对象中的姓名和年龄转换为 Map<String, Integer>
格式,可使用下面的代码:
Map<String, Integer> ageByName = persons.stream()
.collect(Collectors.toMap(Person::getName, Person::getAge));
Copy
Java
该代码首先将流中的元素映射为键值对,其中键是姓名,值是年龄,最后使用 Collectors.toMap()
方法将映射结果存储到 Map<String, Integer>
对象中。
输出结果为:
{Bob=30, CCC=35, Alice=25}
其中,Bob 的年龄是30,CCC 的年龄是35,Alice 的年龄是25。
对象中,某个键和对象对应
如果你想将 List<Person>
对象中的姓名和对象映射为 Map<String, Person>
格式,可使用下面的代码:
Map<String, Person> personByName = persons.stream()
.collect(Collectors.toMap(Person::getName, person -> person));
Copy
Java
该代码首先将流中的元素映射为键值对,其中键是姓名,值是对象本身,最后使用 Collectors.toMap()
方法将映射结果存储到 Map<String, Person>
对象中。
输出结果为:
{Bob=Person{name='Bob', age=30}, CCC=Person{name='CCC', age=35}, Alice=Person{name='Alice', age=25}}
其中,Bob 的对象是 Person{name='Bob', age=30}
,CCC 的对象是 Person{name='CCC', age=35}
,Alice 的对象是 Person{name='Alice', age=25}
。
如果有相同的键,则对应的有策略
如果你想将 List<Person>
对象中的姓名和对象映射为 Map<String, Person>
格式,但是存在相同的姓名,那么你需要定义一个规则来解决冲突。可以使用 toMap()
方法的第三个参数来指定合并规则:
Map<String, Person> personByName = persons.stream()
.collect(Collectors.toMap(
Person::getName, // 键:姓名
person -> person, // 值:对象本身
(p1, p2) -> p2 // 同名的情况下,选择新对象覆盖旧对象
));
Copy
Java
在这个例子中,我们使用了 (p1, p2) -> p2
作为合并规则,表示对于相同的键,返回新值覆盖旧值。 这样,对于 Alice 这个键,会获取到最后一个 Person 对象,即 Person{name='Alice', age=35}
。
输出结果为:
{Bob=Person{name='Bob', age=30}, Alice=Person{name='Alice', age=35}}
其中,Bob 的对象是 Person{name='Bob', age=30}
,Alice 的对象是 Person{name='Alice', age=35}
。
默认使用名字分组
如果要按照名字进行分组,可以使用以下代码:
List<Person> persons = Arrays.asList(
new Person("John", 20),
new Person("Mary", 25),
new Person("Alice", 30),
new Person("Jack", 20)
);
比如这个, 按照名字 怎样分组?
Map<String, List<Person>> personsByName = persons.stream()
.collect(Collectors.groupingBy(Person::getName));
上述代码中,我们调用了stream()方法将persons列表转换为一个Stream对象,然后使用groupingBy方法传入了Person::getName方法引用作为参数。最终返回的是一个Map>对象,其中key表示分组属性(即名字),value表示该分组后的所有元素。
需要注意的是,Person类需要有一个getName()方法用于获取名字属性。如果不存在该方法,则需要替换为对应的属性名称,例如:
Map<String, List<Person>> personsByName = persons.stream()
.collect(Collectors.groupingBy(p -> p.name));
这里我们使用了lambda表达式,p -> p.name表示获取Person类中的name属性值作为分组属性。
来对年龄进行分组统计 输出 Map<String, Long>
Map<String, Long> ageCounts = persons.stream()
.collect(Collectors.groupingBy(Person::getName, Collectors.counting()));
Copy
Java
上述代码中,我们首先使用stream()方法将List转换成一个流(stream),然后使用collect()方法将这些人按照名字分组并进行统计。
其中groupingBy()用于根据名字对人群进行分组,Collectors.counting()用于统计每个分组中有多少人。注意,这里使用了Person::getName作为分组条件,所以Map的键是名字,值是该名字对应的人数。
最终结果是一个Map,键是名字,值是该名字对应的年龄。
### 如果能找到则输出某个值,否则判空,或者异常
Integer age = persons.stream()
.filter(p -> p.getName().equals("Bob"))
.findFirst()
.map(Person::getAge)
.orElse(null);
System.out.println("Bob的年龄是:" + age);
int iAge2 = optionalBob.map(Person::getAge).orElseThrow(() -> new RuntimeException("未找到符合条件的Person对象")); // 如果找不到Person对象,抛出异常
**** 可以使用Java 8的stream和filter方法来实现对Bob的筛选和查询,具体代码如下:
Optional<Person> bob = persons.stream()
.filter(p -> p.getName().equals("Bob"))
.findFirst();
if (bob.isPresent()) {
System.out.println("Bob的年龄是:" + bob.get().getAge());
} else {
System.out.println("不存在名为Bob的人");
}
Java
上述代码中,我们使用stream()方法将List转换成一个流(stream),然后使用filter()方法对流中的每个Person对象进行判断。
在这里,我们使用p -> p.getName().equals("Bob")作为判断条件,意思是筛选出名字等于"Bob"的人。然后,我们使用findFirst()方法获取符合条件的第一个Person对象。
由于结果可能为空,因此我们需要使用Optional类来包装结果。如果存在名为Bob的人,我们使用System.out.println()方法输出他的年龄;否则,我们输出一个提示信息,表明不存在名为Bob的人。
本作品采用《CC 协议》,转载必须注明作者和本文链接