本菜鸟在日常工作时,不是在对集合处理,就是在对集合操作的路上。看破红尘,遁入码门,一切都在修行。那对我来说代码的编写就很重要很重要,可是就是这集合的处理操作让人头疼不已,怎么处理集合会显得更高级,操作更“装逼”呢,下面就是本菜鸟学来在日常慢慢使用的“神器”。
Java8新特性中的Stream API ,便是简洁高效处理集合数据的方式, 不仅可读性较高, 且特别在数据的过滤、转换、聚合时使得操作更简单方便快捷。Stream API根据平时的需求开发中大概两方面,一个作为中间操作使用,一个是最终使用。
有的人肯定直接用不想底层,不在做更多的介绍,底层需要日后平时没事更多学习了解。
一、中间使用
1、过滤筛选:filter
根据指定条件过滤元素,只保留符合条件的,其中Lambda 表达式 s -> s.contains("大")
用于判断字符串是否包含字符"大"。
List<String> tempList = Arrays.asList("刘一手", "杜子腾", "林大蛋", "Ekko");
List<String> resList = tempList.stream()
.filter(s -> s.contains("大"))
.collect(Collectors.toList());
输出:[林大蛋]
但collect(Collectors.toList())是最终操作
2、拼接赋值:map(函数)
对每个元素执行映射操作, 将元素转换成另一种类型。
List<String> tempList = Arrays.asList("刘一手", "杜子腾", "林大蛋", "Ekko");
List<String> resList = tempList.stream()
.map(s -> "姓名: " + s)
.collect(Collectors.toList());
输出:[姓名: 刘一手, 姓名: 杜子腾, 姓名: 林大蛋, 姓名: Ekko]
.
map(s ->"姓名: " + s)
是简写,详细如下:
.
map(s -> {return "姓名:" + s;})
3、数据聚合:flatMap(函数)
可以将每个元素映射成一个 Stream,把所有生成的 Stream 合并成一个新的Stream。
@Data
static class Personnel {
// 人员姓名
private String name;
// 人员标签
private List<String> tagList;
public Personnel(String name, List<String> tagList) {
this.name = name;
this.tagList = tagList;
}
}
public static void main(String[] args) {
Personnel personA = new Personnel("张三", Arrays.asList("抽烟", "喝酒", "烫头"));
Personnel personB = new Personnel("李斯", Arrays.asList("编码", "喝酒", "踢足球"));
List<Personnel> personnelList = Arrays.asList(personA, personB);
personnelList.stream()
.flatMap(p -> p.getTagList().stream())
.forEach(s -> System.out.print(s + " "));
}
输出:抽烟 喝酒 烫头 编码 喝酒 踢足球
上面创建静态内部类,其中聚合类中的集合数据,后把
List<Personnel>
中的tagList
聚合后进行处理。
4、规则排序:sorted()
元素进行排序,默认按照自然顺序,也可自定义的Comparator来指定排序规则。
public class SortedTest {
public static void main(String[] args) {
List<Integer> numList = Arrays.asList(10, 20, 18, 300, 30, 2);
// ① 默认排序
List<Integer> orderList = numList.stream()
.sorted()
.collect(Collectors.toList());
System.out.printf("① 默认排序: %s%n", orderList);
// ② 自定义排序
List<Integer> orderDescList = numList.stream()
.sorted((x, y) -> {
return y.compareTo(x);
})
.collect(Collectors.toList());
System.out.printf("② 自定义排序: %s%n", orderDescList);
}
}
输出:
① 默认排序: [2, 10, 18, 20, 30, 300]
② 自定义排序: [300, 30, 20, 18, 10, 2]
如果你需要自定义排序规则,可以在自定义的类中实现
Comparator
接口,并使用compare
方法,如下所示:import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class CustomComparatorExample { public static void main(String[] args) { List<Fruit> fruits = Arrays.asList(new Fruit("apple", 100), new Fruit("banana", 200), new Fruit("cherry", 150)); // 使用自定义Comparator进行排序 List<Fruit> sortedFruits = fruits.stream() .sorted(Comparator.comparing(Fruit::getWeight)) .collect(Collectors.toList()); // 输出排序后的列表 sortedFruits.forEach(System.out::println); } } class Fruit { private String name; private int weight; public Fruit(String name, int weight) { this.name = name; this.weight = weight; } public String getName() { return name; } public int getWeight() { return weight; } @Override public String toString() { return "Fruit{" + "name='" + name + '\'' + ", weight=" + weight + '}'; } }
在这个例子中,
Fruit
类有一个getWeight
方法,我们使用Comparator.comparing
来创建一个自定义的Comparator
,然后根据水果的重量进行排序。(提示:AI自动生成,仅供参考)
5、同数去重:distinct()
确保每个元素唯一性,输出:[1, 2, 3]
List<Integer> numList = Arrays.asList(1,1,1,1,2,3,2,2);
List<Integer> distinctList = numList.stream()
.distinct()
.collect(Collectors.toList());
6、限制取值:limit(long n)
返回一个最大包含前n个元素的新Stream,输出:[1, 2, 3, 4]
List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8);
List<Integer> limitList = numList.stream()
.limit(4)
.collect(Collectors.toList());
7、跳过丢弃:skip(long n)
跳过前n个元素,输出:[7, 8]
List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> skipList = numList.stream()
.skip(numList.size() - 2)
.collect(Collectors.toList());
8、调试记录
对每个元素执行一个操作,同时保持Stream的流,可以用于调试或记录Stream中的元素
List<Integer> numList = Arrays.asList(5, 6, 7, 8);
List<Integer> resList = numList.stream()
.peek(System.out::println)
.filter(s -> s == 5)
.peek(s -> System.out.printf("过滤后的:%d%n", s))
.collect(Collectors.toList());
输出:
5
过滤后的:5
6
7
8
二、最终操作
1、循环遍历:forEach(Consumer)
对Stream中的每个元素执行指定的操作,接收一个Consumer(消费者函数)作为参数。它通常用于对Stream中的元素进行输出或执行某些操作,但不会返回任何结果。
// 给公司工资普涨 500
List<Integer> salaryList = Arrays.asList(12000, 20000, 30000, 4000);
salaryList.stream()
.peek(s -> System.out.print("工资普涨前:" + s))
.map(s -> s + 500)
.forEach(s -> {
System.out.println("--工资普涨后:" + s);
});
创建一个工资集合,通过 stream() 方法转换为 Stream 流,中间操作 peek() 方法记录转换前的元素值,后 map() 方法给元素进行转换操作,最后通过,forEach() 方法进行遍历。
输出:
工资普涨前:12000--工资普涨后:12500
工资普涨前:20000--工资普涨后:20500
工资普涨前:30000--工资普涨后:30500
工资普涨前:4000--工资普涨后:4500
2、 数据收集:collect(Collector)
可将元素收集到List、Set、Map等容器中
public static void main(String[] args) {
List<User> userList = Arrays.asList(new User("张三", 2000.5),
new User("李斯", 11000.5),
new User("王二", 12000.5),
new User("张六", 32000.5),
new User("赵公子", 1000000.0));
Map<String, Double> userSalaryMap = userList.stream()
.collect(Collectors.toMap(User::getName, User::getSalary));
userSalaryMap.forEach((k, v) -> {
System.out.printf("姓名:%s,工资:%.2f%n", k, v);
});
}
@Data
@AllArgsConstructor
static class User {
private String name;
private Double salary;
}
输出:
姓名:张三,工资:2000.50
姓名:赵公子,工资:1000000.00
姓名:张六,工资:32000.50
姓名:李斯,工资:11000.50
姓名:王二,工资:12000.50
collect() 方法把元素归集,利用 Collectors.toMap() 收集器转换为 Map ,内部接收遍历每个元素。
Collectors.toMap(User::getName, User::getSalary)是简写,详细的如下:
Collectors.toMap(s -> s.getName(), s -> s.getSalary())
3、最大最小值:min/ max (Comparator)
min,最小值;max,最大值;comparator参数用于确定元素的顺序,以便找到最小值
public static void main(String[] args) {
// 示例整数流
Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);
// 使用min()找到最小值
Optional<Integer> minOptional = integerStream.min(Integer::compareTo);
if (minOptional.isPresent()) {
System.out.println("最小值为: " + minOptional.get());
} else {
System.out.println("流为空.");
}
// 重新创建一个整数流,因为流已被消耗
Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);
// 使用max()找到最大值
Optional<Integer> maxOptional = newIntegerStream.max(Integer::compareTo);
if (maxOptional.isPresent()) {
System.out.println("最大值为: " + maxOptional.get());
} else {
System.out.println("流为空.");
}
}
输出:
最小值为: 1
最大值为: 8
一旦流被消耗就不能再次使用,因此在找到最小值后重新创建了一个整数流来找到最大值。
4、计算个数:count()
public static void main(String[] args) {
// 示例整数流
Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);
// 使用count()计算流中的元素个数
long count = integerStream.count();
// 输出元素个数
System.out.println("元素数量: " + count);
}
输出:元素数量: 5
5、是否满足:anyMatch / allMatch / noneMatch (Predicate)
anyMatch:检查流中是否存在至少一个元素满足给定的条件
allMatch:检查流中的所有元素是否都满足给定的条件
noneMatch:检查流中是否所有元素都不满足给定的条件
public static void main(String[] args) {
// 示例整数流
Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);
// 使用anyMatch()检查是否存在元素大于5
boolean anyGreaterThan5 = integerStream.anyMatch(num -> num > 5);
System.out.println("是否存在元素大于 5 ?" + anyGreaterThan5);
// 重新创建一个整数流,因为流已被消耗
Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);
// 使用allMatch()检查是否所有元素都小于10
boolean allLessThan10 = newIntegerStream.allMatch(num -> num < 10);
System.out.println("所有元素都小于10 ? " + allLessThan10);
// 重新创建一个整数流,因为流已被消耗
Stream<Integer> newestIntegerStream = Stream.of(1, 5, 3, 8, 2);
// 使用noneMatch()检查是否没有元素等于10
boolean noneEqualTo10 = newestIntegerStream.noneMatch(num -> num == 10);
System.out.println("是否没有元素等于 10 ? " + noneEqualTo10);
}
输出:
是否存在元素大于 5 ?true
所有元素都小于10? true
是否没有元素等于 10 ? true
6、findFirst() / findAny()
findFirst():流中查找第一个满足条件的元素
findAny():流中查找任意一个满足条件的元素
public static void main(String[] args) {
// 示例整数流
Stream<Integer> integerStream = Stream.of(1, 5, 3, 8, 2);
// 使用findFirst()找到第一个元素
Optional<Integer> firstElementOptional = integerStream.findFirst();
if (firstElementOptional.isPresent()) {
System.out.println("发现第一个元素: " + firstElementOptional.get());
} else {
System.out.println("流为空!");
}
// 重新创建一个整数流,因为流已被消耗
Stream<Integer> newIntegerStream = Stream.of(1, 5, 3, 8, 2);
// 使用findAny()找到任意一个元素
Optional<Integer> anyElementOptional = newIntegerStream.findAny();
if (anyElementOptional.isPresent()) {
System.out.println("找到任意一个元素: " + anyElementOptional.get());
} else {
System.out.println("流为空!");
}
}
输出:
发现第一个元素: 1
找到任意一个元素: 1
程序员鱼丸,以上学习主要来自这个位大佬。
革命尚未成功,本菜鸟仍需努力
三、Java Stream类常见用法
1、基本转换
List<String> nameList = students.stream()
.map(Student::getName)
.collect(Collectors.toList());
2、过滤加基本转换
List<String> above90Names = students.stream()
.filter(t->t.getScore()>90)
.map(Student::getName)
.collect(Collectors.toList());
3、中间操作distinct
List<String> list = Arrays.asList(new String[]{"abc", "def", "hello", "Abc"});
List<String> retList = list.stream()
.filter(s->s.length()<=3)
.map(String::toLowerCase)
.distinct()
.collect(Collectors.toList());
4、中间操作sorted
//返回 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(类::属性一));
//返回 对象集合以类属性一降序排序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序
//返回 对象集合以类属性一升序 属性二升序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));
//返回 对象集合以类属性一降序 属性二升序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序
//返回 对象集合以类属性一降序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序
//返回 对象集合以类属性一升序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序
Comparator.comparing(类::属性一).reversed();
Comparator.comparing(类::属性一,Comparator.reverseOrder());
两种排序是完全不一样的,一定要区分开来:1是得到排序结果后再排序,2是直接进行排序,很多人会混淆导致理解出错,2更好理解,建议使用2
5、中间操作skip/limit
List<Student> list = students.stream()
.sorted(Comparator.comparing(Student::getScore).reversed())
.skip(2).limit(3)
.collect(Collectors.toList());
6、中间操作map ToLong/ToInt/ToDouble
double sum = students.stream().mapToDouble(Student::getScore).sum();
7、终端操作max/min
Student student = students.stream().max(Comparator.comparing(Student::getGrade)).get();
Student student = students.stream().min(Comparator.comparing(Student::getGrade).reversed()).get();
8、容器收集toMap
Map<String, Double> nameScoreMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getScore, (oldValue, value)->value));
Map<String, Student> byIdMap = students.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (oldValue, value)->value));
Map<String, Integer> strLenMap = Stream.of("abc", "hello", "abc").collect(Collectors.toMap(Function.identity(), t->t.length(), (oldValue, value)->value));
9、分组
//将学生流按照年级进行分组
Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getGrade));
//统计每个年级的学生个数
Map<String, Long> gradeCountMap = students.stream().collect(groupingBy(Student::getGrade, counting()));
//统计一个单词流中每个单词的个数,按出现顺序排序
Map<String, Long> wordCountMap = Stream.of("hello", "world", "abc", "hello").collect( groupingBy(Function.identity(), LinkedHashMap::new, counting()));
//对学生按年级分组,得到学生名称列表
Map<String, List<String>> gradeNameMap = students.stream().collect(groupingBy(Student::getGrade, mapping(Student::getName, toList())));
//将学生按年级分组,分组内的学生按照分数由高到低进行排序
Map<String, List<Student>> gradeStudentMap = students.stream().collect(groupingBy(Student::getGrade, collectingAndSort(toList(), Comparator.comparing(Student::getScore).reversed())));
//将学生按年级分组,分组后,每个分组只保留不及格的学生(低于60分)
Map<String, List<Student>> gradeStudentMap = students.stream()
.collect(groupingBy(Student::getGrade, collectingAndFilter(toList(), t->t.getScore()<60)));
hellosc01感谢前辈,嘿哈!!!
以后再完善