java8函数式编程学习(三)- Stream流
3. Stream流
3.1 概述
java8的Stream使用的是函数式变成模式,如同它的名字一样,它可以被用来对集合和数组进行链状流式的操作。
可以方便的让我们对集合和数组进行操作。
3.2 案例准备
Author、Book类、生成数据类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode // 用于后期去重使用
public class Author {
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Book {
private Long id;
private String name;
private String category;
private Integer score;
private String intro;
}
public class StreamDemo {
public static List<Author> getAuthors() {
// 数据初始化
Author author1 = new Author(1L, "蒙多", 33, "测试人1", null);
Author author2 = new Author(2L, "易大师", 24, "测试人2", null);
Author author3 = new Author(3L, "无形之刃", 12, "测试人3", null);
Author author4 = new Author(3L, "无形之刃", 12, "测试人3", null);
// 书籍列表
List<Book> bookList1 = new ArrayList<>();
List<Book> bookList2 = new ArrayList<>();
List<Book> bookList3 = new ArrayList<>();
bookList1.add(new Book(1L, "光辉", "哲学,爱情", 88, "照亮你"));
bookList1.add(new Book(2L, "仇恨", "哲学,心里", 99, "杀死你"));
bookList2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你领略世界的尽头"));
bookList2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你领略世界的尽头"));
bookList2.add(new Book(4L, "吹或不吹", "个人传记,爱情", 56, "哲学家的爱情观"));
bookList3.add(new Book(5L, "你的剑", "玄幻", 56, "武者披荆斩棘"));
bookList3.add(new Book(6L, "我的剑", "哲学", 100, "我的剑就是你的剑"));
bookList3.add(new Book(6L, "我的剑", "哲学", 100, "我的剑就是你的剑"));
author1.setBooks(bookList1);
author2.setBooks(bookList2);
author3.setBooks(bookList3);
author4.setBooks(bookList3);
ArrayList<Author> authorList = new ArrayList<>(Arrays.asList(author1, author2, author3, author4));
return authorList;
}
}
3.3 快速入门
3.3.1 需求
调用getAuthors获取作家集合。现在需要打印所有年龄小于18的作家的名称,并且要去重
3.3.2 实现
List<Author> authorList = StreamDemo.getAuthors();
authorList.stream() // 转化成流
.distinct() // 去重
.filter(author -> author.getAge() < 18) // 筛选条件
.forEach(author -> System.out.println(author)); // 遍历打印
3.4 常用操作
3.4.1 创建流
// 单列集合:集合对象.stream()
List<Author> authorList = StreamDemo.getAuthors();
Stream<Author> authorStream = authorList.stream();
// 数组:Arrays.stream(数组)、Stream.of(数组)
Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> integerStream = Stream.of(arr);
// 双列集合:转化成单列集合后在创建
Map<String, Integer> map = new HashMap<>();
map.put("蜡笔小新", 19);
map.put("小黑", 17);
map.put("狗蛋", 16);
Stream<Map.Entry<String, Integer>> mapStream = map.entrySet().stream();
3.4.2 中间操作
filter 可以对流中的元素进行条件过滤,符合过滤条件的才能留在流中
// 例子:打印所有姓名长度大于1的作家的姓名
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.filter(author -> author.getName().length() > 1)
.forEach(author -> System.out.println(author.getName()));
map 可以对流中的元素进行计算和转换
// 例子:打印所有作家的姓名
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.map(author -> author.getName() + "-转化")
.forEach(name -> System.out.println(name));
authors.stream()
.map(author -> {
author.setName(author.getName() + "-转化");
return author;
})
.forEach(author -> System.out.println(author));
distinct 可以去除流中重复操作
注意:distinct方法依赖Object的equals方法来判断是否是相同对象,所以要注意重写equals方法
// 例子:打印所有作家的名字,不能有重复元素
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.distinct()
.map(author -> author.getName())
.forEach(name -> System.out.println(name));
authors.stream()
.distinct()
.forEach(author -> System.out.println(author.getName()));
sorted 可以对流中的元素进行排序
注意:如果调用空参的sorted()方法,需要流中的元素实现了Comparable接口,重写比较方法
// 例子:对流中的元素按照年龄进行排序,并且不能有重复元素
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.forEach(author -> System.out.println(author));
limit 可以设置流的最大长度,超出的部分将被抛弃
// 例子:对流元素按年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.limit(2)
.forEach(author -> System.out.println(author));
skip 跳过流中的前n个元素,返回剩下的元素
// 例子:打印除了年龄最大作家外的其他作家,不能有重复元素,并且按照年龄排序
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.skip(1)
.forEach(author -> System.out.println(author));
flatMap map只能把一个对象转化成另一个对象来作为流中的元素,而flatMap可以把一个对象转化成多个对象作为流中的元素
// 例子1:打印所有书籍的名字。要求对重复元素进行去重
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.forEach(book -> System.out.println(book));
// 例子2:打印现有数据的所有分类,要求对分类进行去重,不能出现哲学,爱情这种格式
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(category -> System.out.println(category));
3.4.3 终结操作
forEach 对流种元素进行遍历操作,通过传入的参数去指定对遍历的元素进行什么操作
// 例子:输出所有作家的名字
List<Author> authors = StreamDemo.getAuthors();
authors.stream()
.forEach(author -> System.out.println(author.getName()));
authors.stream()
.map(author -> author.getName())
.distinct()
.forEach(name -> System.out.println(name));
count 获取当前流中元素的个数
// 例子:打印这些作家所出书籍的数目,注意删除重复元素
List<Author> authors = StreamDemo.getAuthors();
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
max&min 获取流中的最大值和最小值
// 例子:分别获取这些作家的所出书籍的最高分和最低分并打印
List<Author> authors = StreamDemo.getAuthors();
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
System.out.println(max.get());
Optional<Integer> min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.min((o1, o2) -> o1 - o2);
System.out.println(min.get());
max&min 获取流中的最大值和最小值
// 例子:分别获取这些作家的所出书籍的最高分和最低分并打印
List<Author> authors = StreamDemo.getAuthors();
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
System.out.println(max.get());
Optional<Integer> min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getScore())
.min((o1, o2) -> o1 - o2);
System.out.println(min.get());
collect 把当前流转化成一个集合
// 例子1:获取一个存放所有作者名字的list集合
List<Author> authors = StreamDemo.getAuthors();
List<String> names = authors.stream()
.map(author -> author.getName())
.distinct()
.collect(Collectors.toList());
System.out.println(names);
// 例子2:获取一个所有书名的set集合
Set<String> bookSet = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.map(book -> book.getName())
.collect(Collectors.toSet());
System.out.println(bookSet);
// 例子3:获取一个map集合,key为作者名,value为List<Book>
Map<String, List<Book>> map = authors.stream().distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
Map<String, List<Book>> map1 = authors.stream().distinct()
.collect(Collectors.toMap(Author::getName, Author::getBooks));
for (Map.Entry<String, List<Book>> stringListEntry : map.entrySet()) {
System.out.println("key= " + stringListEntry.getKey() + " and value= " + stringListEntry.getValue());
}
查找与匹配
allMatch 可以用来判断是否都符合匹配条件,结果为boolen,都符合为true
// 例子:判断所有作家是否都是成年人
List<Author> authors = StreamDemo.getAuthors();
boolean allMatch = authors.stream()
.allMatch(author -> author.getAge() >= 18);
System.out.println(allMatch);
noneMatch 判断流中的元素是否都不符合匹配条件,结果为boolen,都不符合为true
// 例子:判断作家年龄都没超过100岁
List<Author> authors = StreamDemo.getAuthors();
boolean noneMatch = authors.stream()
.noneMatch(author -> author.getAge() > 100);
System.out.println(noneMatch);
findAny 获取流中任意一个元素。该方法无法保证获取的一定是流中的第一个元素
// 例子:获取任意一个大于18的作家,如果存在就输出他的名字
List<Author> authors = StreamDemo.getAuthors();
Optional<Author> optionalAnyAuthor = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();
optionalAnyAuthor.ifPresent(author -> System.out.println(author.getName()));
findFirst 获取流中第一个元素
// 例子:获取年龄最小的作家名字
List<Author> authors = StreamDemo.getAuthors();
Optional<Author> first = authors.stream()
.sorted((author1, author2) -> author1.getAge() - author2.getAge())
.findFirst();
first.ifPresent(author -> System.out.println(author.getName()));
reduce 归并
对流中的数据按照你制定的计算方式计算出一个结果.(缩减操作);reduce的作用是吧stream中的元素组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算
// 例子1:使用reduce求所有作者年龄的和
List<Author> authors = StreamDemo.getAuthors();
Integer sum = authors.stream()
.map(author -> author.getAge())
.distinct()
.reduce(0, (result, element) -> result + element);
System.out.println(sum);
// 例子2:使用reduce求所有作者中年龄的最大值
Integer ageMax = authors.stream()
.map(author -> author.getAge())
.distinct()
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println(ageMax);
Optional<Integer> ageMaxOptional = authors.stream()
.map(author -> author.getAge())
.distinct()
.reduce((result, element) -> result > element ? result : element);
ageMaxOptional.ifPresent(age -> System.out.println(age));
// 例子3:使用reduce求所有作者中年龄的最小值
Integer ageMin = authors.stream()
.map(author -> author.getAge())
.distinct()
.reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
System.out.println(ageMin);
Optional<Integer> ageMinOptional = authors.stream()
.map(author -> author.getAge())
.distinct()
.reduce( (result, element) -> result > element ? element : result);
ageMinOptional.ifPresent(age -> System.out.println(age));
3.5 注意事项
- 惰性求值(如果没有终结操作,中间操作是不会得到执行的)
- 流是一次性的(一旦一个流对象经过一个终结操作以后,这个流就不能再被使用)
- 不会影响原数据(一般情况下流操作以后不会影响原来集合中的元素,这也是我们所期望的)
欢迎一起来学习和指导,谢谢关注!
本文来自博客园,作者:xiexie0812,转载请注明原文链接:https://www.cnblogs.com/mask-xiexie/p/16075552.html