注意前提条件: 环境一定是JDK1.8
现在大部分都是1.8的环境,同样也要学习、使用、对其认知,可以使我们开发效率提高,不至于别人请教的时候不清楚。
/**
* @ClassName User
* @Description 用户
* @Author QiMing
* @Date 2021/3/27 8:46
* @Version 1.0
*/
@Data
public class User {
private long id;
/**
* 名字
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 邮箱
*/
private String email;
/**
* 爱好
*/
private String hobby;
public User(){
super();
}
public User(long id, String name, int age, String email, String hobby) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
this.hobby = hobby;
}
}
/**
* @Title: main
* @Description: // 测试使用Stream
* @Param: [args]
* @Return: void
* @Date: 2021/3/27 9:00
* @Author: QiMing
*/
public static void main(String[] args) {
List<User> users = Lists.newArrayList();
users.add(new User(1, "大宝", 26, "1234@qq.com", "插花"));
users.add(new User(2, "二宝", 24, "2345@qq.com", "茶艺"));
users.add(new User(3, "三宝", 24, "3456@qq.com", "学习"));
users.add(new User(4, "四宝", 20, "4567@qq.com", "旅行"));
users.add(new User(5, "五宝", 18, "5678@qq.com", "健身"));
users.add(new User(6, "小宝", 5, "6789@qq.com", "画画"));
users.add(new User(7, "baby", 18, "7890@qq.com", "美妆"));
users.stream().forEach((User u) -> {
// 年龄等于18的 email 设置空
if (18 == u.getAge()) {
u.setEmail(null);
}
});
// Lambda 遍历
users.forEach((User u) -> {
System.out.println(u);
});
System.out.println("双冒号格式");
users.forEach(System.out::println);
/**
* 执行结果:
* 双冒号格式
* User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花)
* User(id=2, name=二宝, age=24, email=2345@qq.com, hobby=茶艺)
* User(id=3, name=三宝, age=24, email=3456@qq.com, hobby=学习)
* User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行)
* User(id=5, name=五宝, age=18, email=null, hobby=健身)
* User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画)
* User(id=7, name=baby, age=18, email=null, hobby=美妆)
*/
// Lambda 简化了代码,但减少了代码的可读性
/**
* Stream
* Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
* Stream API可以极大提高我们的效率,开发过程中写出高效率、干净、简洁的代码。
* 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
*
* 特性:
* 1.不是数据结构:它没有内部存储,它只是用操作管道从source(数据结构、数组、generator function、IO channel)抓取数据。
* 它也绝不修改自己所封装的底层数据结构。例如 Stream的filter 操作产生一个不包含被过滤元素的新Stream,而不是source删除那些元素。
* 2.不支持索引访问:但是很容易生成数组或者 List 。
* 3.惰性化:很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。Intermediate 操作永远是惰性化的。
* 4.并行能力。当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
* 5.可以是无限的:集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。
*
* 注意:所有Stream 的操作必须以lambda 表达式为参数。
*
* Stream 流操作类型:
* 1.ntermediate:一个流可以后面跟随零个或多个 intermediate 操作。
* 其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
* 2.Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。
* 所以这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
*/
/**
* Stream 过滤示例
* 在没使用Stream之前 我们一般采用 for 循环过滤,处理数据繁琐
*/
System.out.println("未过滤的数据 ---" + users);
// 执行结果: 未过滤的数据 ---[User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花), User(id=2, name=二宝, age=24, email=2345@qq.com, hobby=茶艺), User(id=3, name=三宝, age=24, email=3456@qq.com, hobby=学习), User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行), User(id=5, name=五宝, age=18, email=null, hobby=健身), User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画), User(id=7, name=baby, age=18, email=null, hobby=美妆)]
// for循环过滤
List<User> list = Lists.newArrayList();
for (User user : users) {
if (24 != user.getAge()) {
list.add(user);
}
}
System.out.println("for过滤之后的数据 ---" + list);
// 执行结果: for过滤之后的数据 ---[User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花), User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行), User(id=5, name=五宝, age=18, email=null, hobby=健身), User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画), User(id=7, name=baby, age=18, email=null, hobby=美妆)]
// Stream 方式
List<User> collect = users.stream().filter((User u) ->
(24 != u.getAge())).collect(Collectors.toList());
System.out.println("Stream 过滤后的数据 ---" + collect);
// 执行结果: Stream 过滤后的数据 ---[User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花), User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行), User(id=5, name=五宝, age=18, email=null, hobby=健身), User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画), User(id=7, name=baby, age=18, email=null, hobby=美妆)]
/**
* Stream 流之间的转换
* 注意一个Stream流只可以使用一次,这段代码为了简洁而重复使用了数次,
* 因此会抛出 stream has already been operated upon or closed 异常。
*/
try {
Stream stream = Stream.of("say", "hello", "world");
// 转换数组
Object[] objects = stream.toArray(String[]::new);
// 转换成 Collection
List list1 = (List) stream.collect(Collectors.toList());
List list2 = (List) stream.collect(Collectors.toCollection(ArrayList::new));
Set set = (Set) stream.collect(Collectors.toSet());
// 转换成String
String toString = stream.collect(Collectors.joining()).toString();
} catch (Exception e) {
e.printStackTrace();
}
/**
* Stream 流的map使用
*/
// 转换大写
List<String> stringList = Arrays.asList("java", "php", "vue");
System.out.println("stringList转换之前" + stringList);
// 执行结果: stringList转换之前[java, php, vue]
List<String> stringList1 = stringList.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println("stringList转换之后" + stringList1);
// 执行结果: stringList转换之后[JAVA, PHP, VUE]
// 转换数据类型
List<String> stringList2 = Arrays.asList("1", "2", "3");
System.out.println("转换之前的数据:" + stringList2);
// 执行结果: 转换之后的数据:[1, 2, 3]
List<Integer> integerList = stringList2.stream().map(Integer::valueOf).collect(Collectors.toList());
System.out.println("转换之后的数据:" + integerList);
// 执行结果: 转换之后的数据:[1, 2, 3]
// 获取平方
List<Integer> squares = Arrays.asList(new Integer[]{1, 2, 3, 4, 5});
List<Integer> squareList = squares.stream().map(n -> (n * n)).collect(Collectors.toList());
System.out.println("平方的数据:" + squareList);
/**
* 执行结果:平方的数据:[1, 4, 9, 16, 25]
*/
/**
* Stream流的filter使用
* filter方法用于通过设置的条件过滤出元素。
* 通过与 findAny 得到 if/else 的值
*/
List<String> names = Arrays.asList("张三", "李四", "王二", "麻子");
String filter1 = names.stream().filter(str -> "李四".equals(str)).findAny().orElse("找不到!");
String filter2 = names.stream().filter(str -> "baby".equals(str)).findAny().orElse("找不到!");
System.out.println("stream 过滤之后 1:" + filter1);
System.out.println("stream 过滤之后 2:" + filter2);
/**
* 执行结果:
* stream 过滤之后 1:李四
* stream 过滤之后 2:找不到!
*/
// 年龄不等于24的年龄总和
int sum = users.stream().filter((User u) -> 24 != u.getAge()).mapToInt(u -> u.getAge()).sum();
System.out.println("年龄不等于24的年龄总和" + sum);
// 执行结果: 年龄不等于24的年龄总和87
/**
* Stream流的flatMap使用
* flatMap 方法用于映射每个元素到对应的结果,一对多。
* 从句子中得到单词
*/
String words = "You look perfect";
List<String> wordsList = Lists.newArrayList();
wordsList.add(words);
List<String> collect1 = wordsList.stream().flatMap(str -> Stream.of(str.split(" "))).filter(word -> word.length() > 0).collect(Collectors.toList());
System.out.println("单词: ");
collect1.forEach(System.out::println);
/**
* 执行结果:
* You
* look
* perfect
*/
/**
* Stream流的limit使用
* limit 方法用于获取指定数量的流。
* 获取前n条数的数据
*/
Random rd = new Random();
System.out.println("取到的前四条数据:");
rd.ints().limit(4).forEach(System.out::println);
/**
* 执行结果:
* 240251717
* -119437735
* 786951197
* -966403468
*/
// 结合skip使用得到需要的数据
// skip表示的是扔掉前n个元素
// 取前3条数据,但是扔掉了前面的2条,可以理解为拿到的数据为 2<=i<3 (i 是数值下标)
List<String> stringList3 = users.stream().map(User::getName).limit(3).skip(2).collect(Collectors.toList());
System.out.println("截取之后的数据:" + stringList3);
// 执行结果: 截取之后的数据:[三宝]
/**
* Stream流的sort使用
* sorted方法用于对流进行升序排序。
* 随机取值排序
*/
rd.ints().limit(3).sorted().forEach(System.out::println);
/**
* 执行结果:
* -818868489
* -634087012
* -464860067
*/
// tips:先获取在排序效率会更高!
// 普通排序
List<User> collect2 = users.stream().sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).limit(3).collect(Collectors.toList());
System.out.println("排序数据:" + collect2);
// 优化排序取值
List<User> collect3 = users.stream().limit(3).sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).collect(Collectors.toList());
System.out.println("优化后的排序" + collect3);
/**
* Stream流的peek使用
* peek对每个元素执行操作并返回一个新的Stream
* 双重操作
*/
System.out.println("peek使用:");
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("转换之前: " + e))
.map(String::toUpperCase).peek(e -> System.out.println("转换之后: " + e)).collect(Collectors.toList());
/**
* 执行结果:
* 转换之前: three
* 转换之后: THREE
* 转换之前: four
* 转换之后: FOUR
*/
/**
* Stream流的parallel使用
* parallelStream 是流并行处理程序的代替方法。
* 示例:获取空字符串的数量
*/
List<String> strings = Arrays.asList("a", "", "c", "", "e", "", " ");
// 获取空字符串的数量
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的个数:" + count);
// 执行结果: 空字符串的个数:3
/**
* Stream流的max/min/distinct使用
*/
// 得到最大值 最小值
List<String> names1 = Arrays.asList("zhangsan", "lisi", "wanger", "mazi");
int maxLines = names1.stream().mapToInt(String::length).max().getAsInt();
int minLines = names1.stream().mapToInt(String::length).min().getAsInt();
System.out.println("最长字符的长度:" + maxLines + ",最短字符的长度:" + minLines);
// 执行结果: 最长字符的长度:8,最短字符的长度:4
// 得到去重之后的数据
String lines = "she say hello say hello";
List<String> list14 = new ArrayList();
list14.add(lines);
List<String> wordLists = list14.stream().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0)
.map(String::toLowerCase).distinct().sorted().collect(Collectors.toList());
System.out.println("去重复之后:" + wordLists);
// 执行结果: 去重复之后:[hello, say, she]
// 对象某一个值去重
List<Integer> collect4 = users.stream().map(User::getAge).distinct().collect(Collectors.toList());
System.out.println(collect4);
// 执行结果: [26, 24, 20, 18, 5]
List<User> userList = users.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(
// 这里根据对象的某一个值去重,也可以根据多个 在u.getAge()追加 例如u.getAge()+u.getName()
Comparator.comparing((User u) -> u.getAge()
)
)
),
ArrayList::new)
);
System.out.println(userList);
// 执行结果: [User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画), User(id=5, name=五宝, age=18, email=null, hobby=健身), User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行), User(id=2, name=二宝, age=24, email=2345@qq.com, hobby=茶艺), User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花)]
/**
* Stream流的Match使用
* allMatch:Stream 中全部元素符合则返回 true ;
* anyMatch:Stream 中只要有一个元素符合则返回 true;
* noneMatch:Stream 中没有一个元素符合则返回 true。
*/
boolean all = users.stream().allMatch(u -> u.getId() > 3);
System.out.println("是否都大于3:" + all);
boolean any = users.stream().anyMatch(u -> u.getId() > 3);
System.out.println("是否有一个大于3:" + any);
boolean none = users.stream().noneMatch(u -> u.getId() > 3);
System.out.println("是否没有一个大于3的:" + none);
/**
* 执行结果:
* 是否都大于3:false
* 是否有一个大于3:true
* 是否没有一个大于3的:false
*/
/**
* Stream流的reduce使用
* reduce 主要作用是把 Stream 元素组合起来进行操作。
* 字符串连接
*/
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("字符串拼接:" + concat);
// 执行结果: 字符串拼接:ABCD
// 得到最小值
double minValue = Stream.of(-6.0, 1.0, 3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("最小值:" + minValue);
// 执行结果: 最小值:-6.0
// 求和
// 求和, 无起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("有无起始值求和:" + sumValue);
// 执行结果: 有无起始值求和:10
// 求和, 有起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(5, Integer::sum);
System.out.println("有起始值求和:" + sumValue);
// 执行结果: 有起始值求和:15
// 过滤拼接
String filterConcat = Stream.of("a", "B", "c", "D", "e", "F").
filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("过滤和字符串连接:" + filterConcat);
// 执行结果: 过滤和字符串连接:ace
/**
* Stream流的iterate使用
* “iterate 跟 reduce 操作很像,接受一个种子值,和一个UnaryOperator(例如 f)。
* 然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。
* 在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小。
*/
// 生成一个等差队列
Stream.iterate(2, n -> n + 2).limit(5).forEach(x -> System.out.print(x + " "));
// 执行结果: 2 4 6 8 10
System.out.println();
// Stream groupingBy:分组
// 根据年龄分组
Map<Integer, List<User>> listMap = users.stream().collect(Collectors.groupingBy(User::getAge));
Iterator<Map.Entry<Integer, List<User>>> iterator = listMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, List<User>> mapEntry = iterator.next();
System.out.println("age= " + mapEntry.getKey() + "value= " + mapEntry.getValue());
}
/**
* 执行结果:
* age= 18value= [User(id=5, name=五宝, age=18, email=null, hobby=健身), User(id=7, name=baby, age=18, email=null, hobby=美妆)]
* age= 20value= [User(id=4, name=四宝, age=20, email=4567@qq.com, hobby=旅行)]
* age= 5value= [User(id=6, name=小宝, age=5, email=6789@qq.com, hobby=画画)]
* age= 24value= [User(id=2, name=二宝, age=24, email=2345@qq.com, hobby=茶艺), User(id=3, name=三宝, age=24, email=3456@qq.com, hobby=学习)]
* age= 26value= [User(id=1, name=大宝, age=26, email=1234@qq.com, hobby=插花)]
*/
/**
* Stream流的summaryStatistics使用
* IntSummaryStatistics 用于收集统计信息(如count、min、max、sum和average)的状态对象。
* 得到最大、最小、之和以及平均数。
*/
List<Integer> numbers = Arrays.asList(1, 8, 15, 21, 30);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
/**
* 执行结果:
* 列表中最大的数 : 30
* 列表中最小的数 : 1
* 所有数之和 : 75
* 平均数 : 15.0
*/
Integer ageSum = users.stream().collect(Collectors.summingInt((User u) -> u.getAge() < 0 ? 0 : u.getAge()));
// u.getAge() < 0 ? 0 : u.getAge() 此处三目运算是为解决其他类型为null
System.out.println("年龄小于0的年龄之和: " + ageSum);
// 执行结果: 年龄小于0的年龄之和: 135
}