👳我亲爱的各位大佬们好😘😘😘
♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛
👨🔧 个人主页 : 阿千弟
🔥 相关内容👉👉👉 : 都2023年了,如果不会Lambda表达式、函数式编程?你确定能看懂公司代码?前情回顾 :
最近, 我的一些已经入职一段时间的朋友和我说, 在公司中做的最多的业务就是crud, 他最近做的项目是一个h5的中移动一个外包项目, 虽然业务并不复杂, 但是呢数据量非常庞大, 数据库的表很多, 大概四五百张表, 每天的crud写的我这哥们也是很烦.😭😭😭
所以对于这个问题, 建议哥们可以尝试使用 JDK8 新特性 Stream的方式来处理数据, 对于集合, 数组, map的处理很方便, 也很快速, 这时一些朋友会问, stream阅读性不好, 难以读懂, 但是呢你想想别管它咋样, 能有利于咱们开发提高咱们的开发效率就行了呗
为此我专门出来一期关于JDK8 新特性 Stream API 进阶使用的文章, 来帮助咱们经常写crud的朋友来简化开发, 可能你刚开始用的时候很抵触, 但是你用熟练了会觉得它真的爽.🙏🙏🙏希望大家多多支持
1、了解 Stream
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
2、什么是 Stream
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
3、Stream 的操作三个步骤
- 创建 Stream
一个数据源(如:集合、数组),获取一个流
- 中间操作
一个中间操作链,对数据源的数据进行处理
- 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
Stream 的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
1、筛选与切片
方 法| 描 述
|--|--|filter
(Predicate p)
| 接收 Lambda , 从流中排除某些元素。distinct()
| 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素limit
(long maxSize)
| 截断流,使其元素不超过给定数量。skip
(long n)
| 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
2、映射
方 法 | 描 述
|--|--|map
(Function f)
| 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。mapToDouble
(ToDoubleFunction f)
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。mapToInt
(ToIntFunction f)
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。mapToLong
(ToLongFunction f)
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。flatMap
(Function f)
| 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3、排序
方 法 | 描 述
|--|--|sorted()
| 产生一个新流,其中按自然顺序排序sorted
(Comparator comp)
| 产生一个新流,其中按比较器顺序排序
Stream 的终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
1、查找与匹配
方 法 | 描 述
|--|--|allMatch
(Predicate p)
|检查是否匹配所有元素anyMatch
(Predicate p)
|检查是否至少匹配一个元素noneMatch
(Predicate p)
| 检查是否没有匹配所有元素findFirst()
|返回第一个元素findAny()
|返回当前流中的任意元素count()
|返回流中元素总数max
(Comparator c)
|返回流中最大值min
(Comparator c)
|返回流中最小值forEach
(Consumer c)
|内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
2、归约
方 法 | 描 述
|--|--|reduce
(T iden, BinaryOperator b)
| 可以将流中元素反复结合起来,得到一个值。返回 Treduce
(BinaryOperator b)
|可以将流中元素反复结合起来,得到一个值。返回 Optional
3、收集
方 法 | 描 述
|--|--|collect
(Collector c)
|将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法| 返回类型 |作用 |代码
|--|--|--|--|toList
|List |把流中元素收集到List |List emps= list.stream().collect(Collectors.toList());toSet
| Set |把流中元素收集到Set |Set emps= list.stream().collect(Collectors.toSet());toCollection
|Collection| 把流中元素收集到创建的集合 |Collectionemps=list.stream().collect(Collectors.toCollection(ArrayList::new));counting
| Long |计算流中元素的个数| long count = list.stream().collect(Collectors.counting());summingInt
| Integer |对流中元素的整数属性求和| int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));averagingInt
| Double| 计算流中元素Integer属性的平均值 |double avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));summarizingInt
| IntSummaryStatistics |收集流中Integer属性的和。 |IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));joining
|String |连接流中每个字符串 |String str= list.stream().map(Employee::getName).collect(Collectors.joining());maxBy
|Optional| 根据比较器选择最大值 |Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));minBy
|Optional |根据比较器选择最小值 |Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));reducing
|归约产生的类型 |从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值| int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));collectingAndThen
|转换函数返回的类型 |包裹另一个收集器,对其结果转换函数 |int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));groupingBy
|Map> |根据某属性值对流分组,属性为K,结果为V |Map> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));partitioningBy
|Map> |根据true或false进行分区 |Map>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));
下面是工作中经常被用到的案例, 请仔细品味
一些使用场景
1、取出自身重复的数据
@Test
public void test01() {
List<String> list = Arrays.asList(
"g", "k", "f", "k", "g", "b"
);
List<String> li = list.stream().
filter(li1 -> list.stream().filter((li2 -> li2.equals(li1))).count() > 1l)
.collect(toList());
System.out.println(li);
}
2、累加数字
@Test
public void test12() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
}
3、字符串数字集合与整数集合互转
@Test
public void test14() {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(3);
intList.add(4);
List<String> intToStrList = intList.stream().map(String::valueOf).collect(toList());
intToStrList.stream().forEach(str -> {
System.out.println(str);
});
List<String> stringList = new ArrayList<>();
stringList.add("10");
stringList.add("20");
stringList.add("30");
List<String> stringToIntList = stringList.stream().map(String::valueOf).collect(toList());
stringToIntList.stream().forEach(num -> {
System.out.println(num);
});
}
4、取list1与list2的交集
List<String> list1 = Arrays.asList(
"张三", "李四", "李刚", "隋通"
);
List<String> list2 = Arrays.asList(
"熊大", "熊二", "张三", "李刚"
);
//15.取list1与list2的交集
@Test
public void test15() {
List<String> intersection = list1.stream().filter(item -> list2.contains(item)).collect(toList());
System.out.println("---得到交集---");
intersection.parallelStream().forEach(System.out::println);
}
5、取list1与list2的差集
@Test
public void test16() {
List<String> reduce1 = list1.stream().filter(item -> !list2.contains(item)).collect(toList());
System.out.println("---得到差集 reduce1 (list1 - list2)---");
reduce1.parallelStream().forEach(System.out::println);
}
6、取list1和list2的并集
@Test
public void test18() {
List<String> listAll = list1.parallelStream().collect(toList());
List<String> listAll2 = list2.parallelStream().collect(toList());
listAll.addAll(listAll2);
System.out.println("---得到并集 listAll---");
listAll.parallelStream().forEach(System.out::println);
// 去重并集
List<String> listAllDistinct = listAll.stream().distinct().collect(toList());
System.out.println("---得到去重并集 listAllDistinct---");
listAllDistinct.parallelStream().forEach(System.out::println);
System.out.println("---原来的List1---");
list1.parallelStream().forEach(System.out::println);
System.out.println("---原来的List2---");
list2.parallelStream().forEach(System.out::println);
}
7、统计集合重复元素出现次数,并且去重返回hashmap
List<String> list = Arrays.asList(
"b", "g", "d", "d", "t", "j", "d", "a", "c", "f", "a", "b", "s", "w", "w", "b"
);
//19.统计集合重复元素出现次数,并且去重返回hashmap
@Test
public void test19() {
Map<String, Long> map = list.stream().
collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(map);
//由于hashmap无序,所以在排序放入LinkedHashMap里(key升序)
Map<String, Long> sortMap = new LinkedHashMap<>();
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).
forEachOrdered(e -> sortMap.put(e.getKey(), e.getValue()));
System.out.println(sortMap);
//获取排序后map的key集合
List<String> keys = new LinkedList<>();
sortMap.entrySet().stream().forEachOrdered(e -> keys.add(e.getKey()));
System.out.println(keys);
//获取排序后map的value集合
List<Long> values = new LinkedList<>();
sortMap.entrySet().stream().forEachOrdered(e -> values.add(e.getValue()));
System.out.println(values);
}
8、stream循环拿到下标
@Test
public void test21(){
Stream.iterate(0, i -> i + 1).limit(users01.size()).forEach(index -> {
System.out.println(index);
});
}
9、List集合转Map任意一个属性作为键,键相同的对象用集合存储
@Test
public void test22(){
Map<Integer,List<User>> userMap = new HashMap<>();
List<User> userList = null;
for (int i = 0; i < users02.size(); i++) {
User user = users02.get(i);
if(userMap.get(user.getAge())==null){
userList = new ArrayList<>();
}
userList.add(user);
userMap.put(user.getAge(),userList);
}
for(Integer key : userMap.keySet()){
System.out.println("key:"+key+"\n"+"value:"+userMap.get(key));
}
}
10、List集合转Map任意一个属性作为键,键相同的对象用集合存储(Java8新特性实现)
@Test
public void test23(){
Map<Integer, List<User>> listMap = users02.stream().collect(Collectors.groupingBy(User::getAge));
listMap.keySet().forEach(key->{
System.out.println(key);
listMap.get(key).forEach((user)->System.out.println(user));
});
for(Integer key : listMap.keySet()){
System.out.println("key:"+key+"\n"+"value:"+listMap.get(key));
}
}
11、取出users01中age一样的,并算出有几项
@Test
public void test03() {
List<User> userList = users01.stream().filter(user1 -> users01.stream().filter((user2 -> user2.getAge().equals(user1.getAge()))).count() > 1l)
.collect(toList());
userList.stream().forEach((user) -> {
System.out.println(user.getName() + ":" + user.getAge());
});
Map<Object, Object> str = new HashMap<>();
for (User user : userList) {
List<User> collect = userList.stream().filter(d -> d.getAge().equals(user.getAge())).collect(toList());
str.put(user.getAge(), collect.size());
}
for (Object key : str.keySet()) {
String value = str.get(key).toString();//
System.out.println("key:" + key + " value:" + value);
}
}
12、取出users01中的Id在users02中没有的user
/**
* allMatch——检查是否匹配所有元素
* anyMatch——检查是否至少匹配一个元素
* noneMatch——检查是否没有匹配的元素
*/
//4.取出users01中的Id在users02中没有的user
@Test
public void test04() {
//取出所有users02的id
List<Integer> user02Ids = users02.stream().map(User::getId).collect(toList());
System.out.println(user02Ids);
List<User> userList = users01.stream().filter(user1 -> !user02Ids.stream().anyMatch(user02Id -> user1.getId().equals(user02Id))).collect(toList());
userList.stream().forEach((user) -> {
System.out.println(user.getName() + ":" + user.getAge());
});
List<SkuEsModel.Attrs> attrList = baseAttrs.stream().filter(item -> {
// 过滤掉不包含在 searchAttrIds集合中的元素
return idSet.contains(item.getAttrId());
}).collect(Collectors.toList());
}
13、去掉users01name重复的只保留一个
@Test
public void test07() {
List<User> userList = users01.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName()))),
ArrayList::new));
userList.stream().forEach((user) -> {
System.out.println(user.getName() + ":" + user.getAge() + ":" + user.getBirthday());
});
}
14、将Student信息注入到相应的Grade里面的studentList中
List<Student> stuList = Arrays.asList(
new Student(1, "张三", 1),
new Student(2, "李四", 2),
new Student(3, "李飞", 2)
);
List<Grade> gradeList = Arrays.asList(
new Grade(1, "大一"),
new Grade(2, "大二")
);
//13.将Student信息注入到相应的Grade里面的studentList中
@Test
public void test13() {
gradeList.stream().forEach((grade) -> {
List<Student> students = stuList.stream().filter((stu) -> stu.getGradeId().equals(grade.getGradeId())).collect(toList());
grade.setStudentList(students);
});
gradeList.stream().forEach(grade -> {
System.out.println(grade.getGradeName() + "---");
grade.getStudentList().stream().forEach(stu -> {
System.out.println(stu.getStuName());
});
});
}
如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对
spring
,分布式
,云原生
感兴趣的朋友,请多多关注💖💖💖
👨🔧 个人主页 : 阿千弟