概述
Java8的Stream使用的是函数式编程模式,它可以被用来对集合或数组进行链状流式的操作,可以更方便地让我们对集合或数组操作。
使用Stream流程:
创建流 -> 中间操作 -> 终结操作;
注:必须要有终结操作否则中间操作不生效;
数据准备
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode // 用于后期去重使用
public class Author {
private Long id;
// 作者
private String name;
// 年龄
private Integer age;
// 介绍
private String intro;
// 作品
private List<Book> books;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode // 用于后期去重使用
public class Book {
private Long id;
// 书名
private String name;
// 分类
private String category;
// 评分
private Integer score;
// 介绍
private String intro;
}
import com.fw.entity.Author;
import com.fw.entity.Book;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamDemo {
private static List<Author> getAuthors() {
Author author1 = new Author(1L, "雷蒙多", 33, "简介1", null);
Author author2 = new Author(2L, "亚拉索", 15, "简介2", null);
Author author3 = new Author(3L, "易", 14, "简介3", null);
Author author4 = new Author(3L, "易", 14, "简介3", null);
List<Book> books1 = new ArrayList<Book>();
List<Book> books2 = new ArrayList<Book>();
List<Book> books3 = new ArrayList<Book>();
books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用—把刀划分了爱恨"));
books1.add(new Book(2L, "—个人不能死在同—把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"—个哲学家的恋爱观注定很难把他所在的时代理解"));
books3.add(new Book(5L,"你的剑就是我的剑","爱情",56,"无法想象—个武者能对他的伴倡这么的宽容"));
books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
author1.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
return new ArrayList<Author>(Arrays.asList(author1,author2,author3,author4));
}
}
1.Stream快速入门
实现一:
getAuthors()方法获取到作家集合,打印出年龄小于18的作家的名字,并且要注意去重;
public static void main(final String[] args) {
/**
* 实现一:getAuthors()方法获取到作家集合,打印出年龄小于18的作家的名字,并且要注意去重。
*/
List<Author> authors = getAuthors();
authors.stream() // 把集合转换成流
.distinct() // 去处重复的对象数据
.filter(author -> author.getAge() < 18) // 筛选年龄小于18
.forEach(author -> {System.out.println(author.getName());}); // 遍历打印名字
}
补充说明:
在idea中对steram流调试有很好的支持,比如我们在stream处打上断点使用debug模式去执行;
2.Stream常用操作
2.1.创建流
Java中有两类集合,一类是单列集合,父接口为Collection,一类是双列集合(如Map),父接口为Map。根据集合对象的不同,有如下几种创建流的方式:
1.单列集合(List、Set):集合对象.stream()
// 单列集合(List、Set)创建Stream流对象
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
// 创建流对象
list.stream();
set.stream();
2.数组([]):Arrays.stream(数组)或者Stream.of(数组)
Integer [] arr = {1,2,3,4,5};
// 数组创建Stream流对象
Arrays.stream(arr); // 方式一
Stream.of(arr); // 方式二
3.双列集合:转换为单列集合后再创建
// 双列集合
Map<String, Integer> map = new HashMap<>();
map.put("朴树",20);
map.put("张靓颖",18);
map.put("海子",16);
// 双列集合转单列集合
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
// 过滤输出案例
map.entrySet() // 双列集合转为“单列集合”
.stream() // 单列集合转换为流
.filter(entry -> entry.getValue()>=18) // 过滤年龄大于等于18
.forEach(user -> System.out.println("姓名:" + user.getKey() + ",年龄:" + user.getValue())); // 输出姓名、年龄
2.2.中间操作
1.filter
filter(Predicate<? super T> predicate)
对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中;
例:打印所有姓名长度大于1的作家的姓名;
/**
* 打印姓名字段长度大于1的作家姓名;
*/
private static void test1() {
List<Author> authors = getAuthors();
// 获取stream流
authors.stream()
.filter(author -> author.getName().length() > 1) // 过滤获取姓名长度大于1的数据
.forEach(author -> { System.out.println(author.getName());}); // 遍历数据
}
2.map
map(Function<? super T,? extends R> mapper)
对流中的元素进行计算或类型转换;
例:
1.获取authors集合中所有age数据(类型转换Author转Integer);
2.为所有age数据加上10岁(元素计算);
3.为所有age数据拼接"岁!"字符串(类型转换Integer转String);
4.遍历输出;
/**
* 1.获取authors集合中所有age数据(类型转换Author转Integer)
* 2.为所有age数据加上10岁(元素计算)
* 3.为所有age数据拼接"岁!"字符串(类型转换Integer转String)
* 4.遍历输出
*/
private static void test2() {
List<Author> authors = getAuthors();
// 匿名内部类写法
authors.stream() // 获取Stream流
.map(new Function<Author, Integer>() { // 将Author类型的集合,取出Integer类型的age数据放在新的Integer集合中
@Override
public Integer apply(Author author) {
return author.getAge();
}
})
.map(new Function<Integer, Integer>() { // 为所有age数据加上10岁
@Override
public Integer apply(Integer age) {
return age + 10;
}
})
.map(new Function<Integer, String>() { // 将上面得到的Integer集合中的age字段拼接“岁!”转换为字符串类型集合
@Override
public String apply(Integer age) {
return age + "岁!";
}
})
.forEach(age -> System.out.println(age)); // 遍历输出
System.out.println("===================================== 分隔线 =====================================");
// Lambda写法
authors.stream()
.map(author -> author.getAge()) // 1.获取authors集合中所有age数据(将原本Author类型的集合转换为Integer类型的集合【类型转换】)
.map(age -> age + 10) // 2.为所有age数据加上10岁【元素计算】
.map(age -> age + "岁!") // 3.为所有age数据拼接"岁!"字符串(将Integer类型的集合转换为String类型的集合【类型转换】)
.forEach(age -> System.out.println(age)); // 4.遍历输出
}
输出:
3.distinct
去除流中的重复元素;
注意: distinct方法是依赖类中的equals方法来判断是否是相同对象,所以如果要对某个类型的对象进行去重,这个类中必须重写equals()和hashCode()方法。自定义对象默认的equals方法是使用“==”去比较两个对象是否相同,即比较的是两个对象的地址值是否相同。在上面Author类中我们使用lombok@EqualsAndHashCode注解去实现重写equals()和hashCode(),在新版的lombok中@Data注解已包含@EqualsAndHashCode注解,故添加了@Data注解可以不用再添加EqualsAndHashCode注解;
比如下面我们重写equals和hashCode方法去指定我们自己的比较对象是否重复规则;
例:
去重打印所有作者的信息;
/**
* 去重打印所有作者的信息
*/
private static void test3() {
List<Author> authors = getAuthors();
authors.stream()
.distinct() // 根据Author类中重写的equals()和hashCode()规则去重数据
.forEach(author -> System.out.println(author)); // 循环遍历
}
输出:
4.sorted
sorted(Comparator<? super T> comparator)
对流中的元素进行排序;
例:
1.对年龄进行去重;
2.年龄降序排序;
/**
* 1.对年龄进行去重
* 2.年龄降序排序
*/
private static void test4() {
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge()) // 将Author类型的集合,取出Integer类型的age数据放在新的Integer集合中
.distinct() // 去重Integer类型的age集合
.sorted((age1, age2) -> age2 - age1) // 降序排序
.forEach(age -> System.out.println(age)); // 循环遍历
}
5.limit
limit(long maxSize)
设置流的最大长度(指定获取流中元素数量)超出的部分将被舍弃;
例:
1.对年龄进行去重;
2.年龄降序排序;
3.取年龄最大的两个作者;
4.打印年龄最大的两个作者及年龄;
/**
* 1.对年龄进行去重;
* 2.年龄降序排序;
* 3.取年龄最大的两个作者;
* 4.打印年龄最大的两个作者及年龄
*/
private static void test5() {
List<Author> authors = getAuthors();
authors.stream()
.map(author -> { // 类型转换Author转Map(key为age,value为name)
Map<Integer, String> mapData = new HashMap<Integer, String>();
mapData.put(author.getAge(),author.getName());
return mapData;
})
.distinct() // 去重数据
.limit(2) // 取年龄最大的两个作者
.forEach(e -> System.out.println(e)); // 遍历输出
}
输出:
6.skip
skip(long n)
跳过流中前n个元素返回剩下的元素;
例:
1.对年龄进行去重;
2.年龄按照降序排序;
3.返回除了最大年龄外的其它年龄;
/**
* 1.对年龄进行去重;
* 2.年龄按照降序排序;
* 3.返回除了最大年龄外的其它年龄;
*/
private static void test6() {
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge()) // 类型转换Author转Integer
.distinct() // 去重
.sorted((age1, age2) -> age2 - age1) // 降序排序
.skip(1) // 跳过最大年龄
.forEach(age -> System.out.println(age)); // 遍历输出
}
7.flatMap
flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
map只能把一个对象转换成另一个对象来作为流中的元素,而fatMap可以把一个对象转换成多个对象作为流中的元素;
例一:
获取所有作者的书籍并根据书籍名去重;
/**
* 获取所有作者的书籍并根据书籍名去重
*/
private static void test7() {
List<Author> authors = getAuthors();
// 匿名内部类写法
authors.stream() // 获取Stream流
.flatMap(new Function<Author, Stream<Book>>() {
@Override
public Stream<Book> apply(Author author) {
return author.getBooks().stream();
}
}) // 获取每个作者下面的Book集合并将每个作者的Book集合转换为Stream流
.map(book -> book.getName()) // 取出每个Stream流中的Book对象的名字
.distinct() // 名字去重
.forEach(bookName -> System.out.println(bookName)); // 遍历打印
System.out.println("===================================== 分隔线 =====================================");
// Lambda写法
authors.stream() // 获取Stream流
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 获取每个作者下面的Book集合并将每个作者的Book集合转换为Stream流
.map(book -> book.getName()) // 取出每个Stream流中的Book对象的名字
.distinct() // 名字去重
.forEach(bookName -> System.out.println(bookName)); // 遍历打印
}
例二:
打印现有书籍的所有分类,要求对分类进行去重且不能出现多个分类如(哲学,爱情)的格式 ;
/**
* 打印现有书籍的所有分类,要求对分类进行去重且不能出现多个分类如(哲学,爱情)的格式 ;
*/
private static void test8() {
List<Author> authors = getAuthors();
// 匿名内部类写法
authors.stream()
.flatMap(new Function<Author, Stream<Book>>() {
@Override
public Stream<Book> apply(Author author) {
return author.getBooks().stream();
}
})
.flatMap(new Function<Book, Stream<String>>() {
@Override
public Stream<String> apply(Book book) {
return Arrays.stream(book.getCategory().split(","));
}
})
.distinct()
.forEach(category -> System.out.println(category));
System.out.println("===================================== 分隔线 =====================================");
// Lambda写法
authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 获取每个作者下面的Book集合并将每个作者的Book集合转换为Stream流;
.flatMap((Function<Book, Stream<?>>) book -> Arrays.stream(book.getCategory().split(","))) // 获取Stream流中的每个Book对象的“category”并根据“,”拆分转成新的Stream流;
.distinct() // 去重
.forEach(category -> System.out.println(category)); // 遍历
}
2.3.终结操作
1.forEach
forEach(Consumer<? super T> action)
对流中的元素进行遍历操作,可以通过传入的参数指定对遍历到的元素进行什么具体操作;
例:
打印所有作家的名字;
/**
* 打印所有作家的名字
*/
private static void test9() {
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName()) // 取出所有Author对象中的名字
.distinct() // 名字去重
.forEach(name -> System.out.println(name)); // 遍历输出
}
2.count
count()
获取当前流中元素的个数;
例:
统计所有作家书籍的总数(若书籍名相同则去重)
/**
* 统计所有作家书籍的总数(若书籍名相同则去重)
*/
private static void test10() {
List<Author> authors = getAuthors();
long total = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 取出所有作家的书籍转为Stream流
.map(book -> book.getName()) // 取出每个Book对象的名字
.distinct() // 名字去重
.count(); // 统计根据书籍名去重后的书籍总数
System.out.println(total);
}
3.max&min
max(Comparator<? super T> comparator)
通过传入的Comparator对元素进行比较,得到最大值;
min(Comparator<? super T> comparator)
通过传入的Comparator对元素进行比较,得到最小值;
补充说明
max&min函数返回的是Optional对象,需调用get()方法获取到具体的值;
例:
获取所有作家出版书籍的“最高评分”和“最低评分”
/**
* 获取所有作家出版书籍的“最高评分”和“最低评分”
*/
private static void test11() {
List<Author> authors = getAuthors();
// 【获取最高评分】 max函数返回的是一个Optional对象,需调用get()方法获取到具体的值
Optional<Integer> max = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 取出所有作家的书籍转为Stream流
.map(book -> book.getScore()) // 取出每个Book对象的score(评分)
.max((score1, score2) -> score1 - score2); // 定义比较规则获取最大值(max函数返回的是一个Optional对象,需调用get()方法获取到具体的值)
System.out.println(max.get());
// 【获取最低评分】 min函数返回的是一个Optional对象,需调用get()方法获取到具体的值
Optional<Integer> min = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 取出所有作家的书籍转为Stream流
.map(book -> book.getScore()) // 取出每个Book对象的score(评分)
// 特别说明:此处的比较规则和取【最高评分】比较规则相同都是score1 - score2,但因为调用函数的不同(min())故此处取的是最小值
.min((score1, score2) -> score1 - score2); // 定义比较规则获取最小值(min函数返回的是一个Optional对象,需调用get()方法获取到具体的值)
System.out.println(min.get());
}
4.collect
collect(Collector<? super T,A,R> collector)
将当前流转换为一个集合;
在某些场景下,集合通过流处理之后,需要导出为一个新的集合进行使用,这时候就需要使用collect()方法;
例一:
获取一个存放所有作者名字的List集合
/**
* 获取一个存放所有作者名字的List集合
*/
private static void test12() {
List<Author> authors = getAuthors();
List<String> nameList = authors.stream()
.map(author -> author.getName()) // 获取Stream流中每个作者对象的name
.collect(Collectors.toList()); // 将获取到的所有作者name转换为List
System.out.println(nameList);
}
例二:
获取所有书名的Set集合;
/**
* 获取所有书名的Set集合
*/
private static void test13() {
List<Author> authors = getAuthors();
Set<String> bookSet = authors.stream()
.flatMap((Function<Author, Stream<Book>>) author -> author.getBooks().stream()) // 取出所有作家的书籍转为Stream流
.map(book -> book.getName()) // 取出每个Book对象的name
.collect(Collectors.toSet()); // 转换为Set集合
System.out.println(bookSet);
}
例三:
获取一个Map集合(Key为作者名value为List<Book>)
/**
* 获取一个Map集合,map的key为作者名,value为List<Book>。
*/
private static void test14() {
List<Author> authors = getAuthors();
// 写法一:匿名内部类写法
Map<String, List<Book>> resultMap = authors.stream()
.distinct() // 去重
// Collectors.toMap(Function keyMapper,Function valueMapper)两个Function函数参数分别指定Map的Key,Value映射规则
.collect(Collectors.toMap(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName(); // 取出作家的name作为Map的Key
}
}, new Function<Author, List<Book>>() {
@Override
public List<Book> apply(Author author) {
return author.getBooks(); // 取出作家的books集合作为Map的Value
}
}));
System.out.println(resultMap);
System.out.println("======================================== 分割线 ========================================");
// 写法二:Lambda表达式写法
Map<String, List<Book>> resultMap2 = authors.stream()
.distinct() // 去重
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks())); // 指定Map的Key,Value映射规则
System.out.println(resultMap2);
System.out.println("======================================== 分割线 ========================================");
// 写法三:指定当Map中的Key重复时Value的取值策略
Map<String, List<Book>> resultMap3 = authors.stream()
// 指定Map的Key,Value映射规则
.collect(Collectors.toMap(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName(); // 取出作家的name作为Map的Key
}
}, new Function<Author, List<Book>>() {
@Override
public List<Book> apply(Author author) {
return author.getBooks(); // 取出作家的books集合作为Map的Value
}
}, new BinaryOperator<List<Book>>() { // 当Map的Key重复时指定保留哪个Key对应的Value数据
@Override
public List<Book> apply(List<Book> books1, List<Book> books2) {
/**
* return books1;表示保留先出现Key的Value
* return books2;表示保留后出现Key的Value
*/
return books1;
}
}));
System.out.println(resultMap3);
}
5.anyMatch
anyMatch(Predicate<? super T> predicate)
判断流内是否有符合匹配条件的元素,结果为boolean类型。只要有一个元素满足条件就返回true;
例:
判断年龄是否有在29岁以上的作家;
/**
* 判断年龄是否有在29岁以上的作家
*/
private static void test15() {
List<Author> authors = getAuthors();
// 匿名内部类写法
boolean result = authors.stream()
.anyMatch(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 29;
}
});
System.out.println(result);
// Lambda表达式写法
boolean result2 = authors.stream()
.anyMatch(author -> author.getAge() > 29);
System.out.println(result2);
}
6.allMatch
allMatch(Predicate<? super T> predicate)
与anyMatch()类似判断流内是否所有元素都满足匹配条件,结果为boolean类型。当所有元素都满足条件时返回true;
例:
判断所有作者intro字段中是否都包含“简介”字符串;
/**
* 判断所有作者intro字段中是否都包含“简介”字符串
*/
private static void test16() {
List<Author> authors = getAuthors();
// 匿名内部类写法
boolean result = authors.stream()
.allMatch(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getIntro().contains("简介");
}
});
System.out.println(result);
// Lambda表达式写法
boolean result2 = authors.stream()
.allMatch(author -> author.getIntro().contains("简介"));
System.out.println(result2);
}
7.noneMatch
noneMatch(Predicate<? super T> predicate)
与上面两个类似,判断流内是否所有元素都不满足匹配条件,结果为boolean类型。当所有元素都不满足条件时才返回true;
例:
判断每个作者的年龄是否都小于0岁;
/**
* 判断每个作者的年龄是否都小于0岁
*/
private static void test17() {
List<Author> authors = getAuthors();
// 匿名内部类写法
boolean result = authors.stream()
.noneMatch(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() < 0;
}
});
System.out.println(result);
// Lambda表达式写法
boolean result2 = authors.stream()
.noneMatch(author -> author.getAge() < 0);
System.out.println(result2);
}
8.findAny
findAny()
获取流中的任意一个元素(该方法没有办法保证获取的元素一定是流中第一个元素),返回的是一个Optional对象;
例:
获取任意一个年龄大于18的作家(如果存在年龄大于18的作家就输出该作家名字);
/**
* 获取任意一个年龄大于18的作家(如果存在年龄大于18的作家就输出该作家名字)
*/
private static void test18() {
List<Author> authors = getAuthors();
Optional<Author> findAnyObj = authors.stream()
.filter(author -> author.getAge() > 18) // 筛选年龄大于18的数据(筛选结果 有可能有,有可能没有)
.findAny(); // 重筛选结果任取一个元素(该方法返回Optional类型)
// Optional调用ifPresent判断Optional中是否存在该对象
findAnyObj.ifPresent(new Consumer<Author>() {
@Override
public void accept(Author author) {
// 存在则输出作者名字
System.out.println(author.getName());
}
});
}
9.findFirst
findFirst()
获取流中第一个元素,返回一个Optional对象。与findAny()的用法一样;
/**
* 获取一个年龄最小的作家,并输出他的姓名
*/
private static void test19() {
List<Author> authors = getAuthors();
Optional<Author> findFirstObj = authors.stream()
.sorted((obj1, obj2) -> obj1.getAge() - obj2.getAge()) // 年龄升序排序
.findFirst(); // 取升序排序后的第一个元素
findFirstObj.ifPresent(new Consumer<Author>() { // 判断Optional中是否存在Author对象
@Override
public void accept(Author author) {
// Optional中存在Author对象输出作者名字
System.out.println(author.getName());
}
});
}
10.reduce
对流中的数据按照指定的计算方式计算出—个结果。有三种实现:
1.reduce(BinaryOperator<T> accumulator)
返回的是Optional<T>对象;
2.reduce(T identity,BinaryOperator<T> accumulator)
返回的是T类型对象;
3.reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
返回的是<U>U类型对象;
两个参数
reduce(T identity, BinaryOperator<T> accumulator)的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们指定的计算方式依次取出流中的元素和初始化值一起进行计算,并将计算结果再和后面的元素计算;
reduce两个参数内部计算方式如下:
其中identity就是我们通过方法参数传入的初始值,accumulator的apply方法指定计算方式;
例一:
使用reduce计算所有作者年龄和;
/**
* 使用reduce计算所有作者年龄和
*/
private static void test20() {
List<Author> authors = getAuthors();
// 匿名内部类写法
Integer sum = authors.stream()
.map(new Function<Author, Integer>() {
@Override
public Integer apply(Author author) { // 取出所有Author对象的Age数据并转为Stream流
return author.getAge();
}
})
.reduce(0, new BinaryOperator<Integer>() { // 参数一:初始值; 参数二:具体计算方式
@Override
public Integer apply(Integer result, Integer element) { // result:每次计算后的值 element:上面Stream流中的每个age
return result + element;
}
});
System.out.println("匿名内部类写法:" + sum);
System.out.println("======================================== 分割线 ========================================");
// Lambda写法
Integer sum2 = authors.stream()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result + element);
System.out.println("Lambda写法:" + sum2);
}
例二:
使用reduce求所有作者中年龄的最大值;
实际上使用max()也可以实现这个操作,其实max()底层也是调用的reduce()方法;在开发中有时候需要求一些其它类型的统计值,因此还是看看如何使用reduce()来实现 max()的功能;
/**
* 使用reduce求所有作者中年龄的最大值
*/
private static void test21() {
List<Author> authors = getAuthors();
Integer max = authors.stream()
.map(new Function<Author, Integer>() { // 取出所有Author对象的Age数据并转为Stream流
@Override
public Integer apply(Author author) {
return author.getAge();
}
})
.reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() { // 参数一:初始值; 参数二:具体计算方式
@Override
public Integer apply(Integer result, Integer element) {
return result < element ? element : result; // 两数比较取最大值
}
});
System.out.println("匿名内部类写法 = " + max);
System.out.println("======================================== 分割线 ========================================");
Integer max2 = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println("Lambda写法:" + max2);
}
例三:
使用reduce求所有作者中年龄的最小值;
/**
* 使用reduce求所有作者中年龄的最小值
*/
private static void test22() {
List<Author> authors = getAuthors();
Integer min = authors.stream()
.map(new Function<Author, Integer>() { // 取出所有Author对象的Age数据并转为Stream流
@Override
public Integer apply(Author author) {
return author.getAge();
}
})
.reduce(Integer.MAX_VALUE, new BinaryOperator<Integer>() { // 参数一:初始值; 参数二:具体计算方式
@Override
public Integer apply(Integer result, Integer element) { // 两数比较取最小值
return result > element ? element : result;
}
});
System.out.println("匿名内部类写法 = " + min);
System.out.println("======================================== 分割线 ========================================");
Integer min2 = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
System.out.println("Lambda写法:" + min2);
}
单个参数
reduce(BinaryOperator<T> accumulator)的作用和两参的reduce()作用一样,只是将流中的第一个元素作为初始值,而不是传入自定义的初始值,然后依然是按照accumulator的apply()方法定义计算规则;
reduce单个参数内部计算方式如下:
例:
使用reduce单个参数方法求所有作者中年龄的最小值;
/**
* 使用reduce 单个参数方法求所有作者中年龄的最小值
*/
private static void test23() {
List<Author> authors = getAuthors();
// 匿名内部类写法
Optional<Integer> minOptional = authors.stream()
.map(new Function<Author, Integer>() { // 取出所有Author对象的Age数据并转为Stream流
@Override
public Integer apply(Author author) {
return author.getAge();
}
})
.reduce(new BinaryOperator<Integer>() { // 只有一个参数:具体计算方式; 初始值默认为上面Stream流中的第一个元素
@Override
public Integer apply(Integer result, Integer element) {
return result > element ? element : result;
}
});
// 返回Optional对象取出其Optional对象中值
minOptional.ifPresent(new Consumer<Integer>() {
@Override
public void accept(Integer element) {
System.out.println("匿名内部类写法:" + element);
}
});
// Lambda写法
Optional<Integer> minOptional2 = authors.stream()
.map(author -> author.getAge())
.reduce((result, element) -> result > element ? element : result);
// 返回Optional对象取出其Optional对象中值
minOptional2.ifPresent(element -> System.out.println("Lambda写法:" + element));
}
3.Stream流注意事项
1.惰性求值
在对流进行操作时操作不会立即执行,而是等到需要结果时才开始进行计算,即没有终结操作中间操作是不会执行的; 这种延迟计算的特性可以提高性能,因为只有在需要结果时才会触发计算;
2.流是一次性的
Stream流是一次性的,一旦对流执行了终结操作流就会被消耗掉无法再次使用;如果需要对同一组数据进行多个操作可以创建一个新的流来进行操作;
3.不会影响原数据
Stream流的操作不会直接修改原始数据源中的元素,也不会影响原始数据源的结构。所有的流操作都是基于数据源的副本或视图进行的,保证了原始数据的不变性。(除非在流中调用了流中元素对象的setter类似的方法,例如:)
private static void test24() {
List<Author> authors = getAuthors();
authors.stream()
.map(new Function<Author, Object>() {
@Override
public Object apply(Author author) {
author.setAge(author.getAge() + 100); // 在流中调用流中元素的set方法会改变原始对象的值
return author;
}
}).forEach(author -> System.out.println(author));
}