若想了解java8其它新特性,请到 00-java8常用新特性文章索引 阅读。
这篇文章写Stream API 的使用,各个用例中会大量结合 lambda
表达式,如果 函数式接口
和 lambda表达式
玩得不熟的朋友先看看 01-函数式接口和 lambda 表达式 ,如果对 Stream API 的创建、中间操作、终止操作相关接口不熟悉的朋友先看看 05-Stream API 第一篇。
提示: 表格的排版在手机端阅读可能有点乱,表格内容可以尝试手机横屏阅读或者电脑端阅读。
Java常用的内置函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对给定的 T 类型的参数,进行操作, 核心方法: void accept(T t); |
Supplier 供给型接口 | 空参 | T | 获取一个 T 类型的对象 核心方法: T get(); |
Function<T, R> 函数型接口 | T | R | 对 T 类型的参数进行操作,返回 R 类型的结果 核心方法: R apply(T t); |
Predicate 断定型接口 | T | boolean | 判断 T 类型的参数,是否满足某约束,返回 boolean 值 核心方法: boolean test(T t); |
BiFunction<T, U, R> | T, U | R | 对类型为 T, U 参数应用操作, 返回 R 类型的结果。 核心方法为 R apply(T t, U u); |
UnaryOperator (Function子接口) |
T | T | 对类型为T的对象进行一元运算, 并返回T类型的结果。 核心方法为 T apply(T t); |
BinaryOperator (BiFunction 子接口) |
T, T | T | 对类型为T的对象进行二元运算, 并返回T类型的结果。 核心方法为 T apply(T t1, T t2); |
BiConsumer<T, U> | T, U | void | 对类型为T, U 参数应用操作。 核心方法为 void accept(T t, U u) |
ToIntFunction ToLongFunction ToDoubleFunction |
T | int/long/double | T 类型的参数, 返回int\long\double 类型的结果, 核心方法为 int\long\double applyAsInt(T value); |
IntFunction LongFunction DoubleFunction |
int/long/double | R | 参数分别为int、long、double 类型的函数, 返回 R 类型的结果, 核心方法: R apply(int\long\double value); |
1 Stream 创建 API
Stream创建:可通过 数组、Collction 和 Steam类的静态方法来创建一个Stream实例。
定义 Person ,在下面大部分测试用例用到的实体 Bean:
public class Person {
//姓名
private String name;
//年龄
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return getAge() == person.getAge() &&
Objects.equals(getName(), person.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
}
1.1 通过数组创建
/**
* 通过数组创建Stream
*/
@Test
public void testCreateByArray() {
int[] nums = new int[]{0, 1, 5, 2, 4, 8, 9};
Person[] personArray = new Person[]{
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15)
};
// Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
IntStream stream = Arrays.stream(nums);
Stream<Person> personStream = Arrays.stream(personArray);
//打印 stream 中的元素
System.out.println("打印 stream 中的元素: ");
stream.forEach(item -> System.out.print(item + " "));
//打印 personStream 中的元素
System.out.println("\n打印 personStream 中的元素: ");
personStream.forEach(item -> System.out.println(item + " "));
}
运行结果:
打印 stream 中的元素:
0 1 5 2 4 8 9
打印 personStream 中的元素:
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
1.2 通过Collection创建
Collection接口中提供了 stream() 获取顺序流和 parallelStream() 获取并行流的两个方法
顺序流:串行流,流中的元素序列顺序和元素在数据源中的顺序是一样的。
并行流:把元素序列分成多个数据块,并用不同的线程分别处理每个数据块的流
Stream API 可以通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* 通过Collection创建Stream
*/
@Test
public void testCreateByCollection() {
//stream() 将此集合作为数据源,返回一个顺序流
Stream<Person> seqStream = persons.stream();
//parallelStream() 将此集合作为数据源,返回一个并行流
Stream<Person> parallelStream = persons.parallelStream();
//打印顺序流 seqStream 中的元素
System.out.println("打印顺序流 seqStream 中的元素: ");
seqStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));
//并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
// 打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的
System.out.println("\n打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的: ");
parallelStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));
System.out.println("\nsequential()把并行流切换成顺序流,输出: ");
persons.parallelStream()
.sequential()//并行流切换成顺序流
.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));
}
}
运行结果:
打印顺序流 seqStream 中的元素:
线程名main--Person{name='刘一', age=25}
线程名main--Person{name='陈二', age=12}
线程名main--Person{name='张三', age=24}
线程名main--Person{name='李四', age=29}
线程名main--Person{name='王五', age=22}
线程名main--Person{name='赵六', age=15}
线程名main--Person{name='孙七', age=16}
线程名main--Person{name='周八', age=18}
打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的:
线程名main--Person{name='赵六', age=15}
线程名main--Person{name='王五', age=22}
线程名main--Person{name='周八', age=18}
线程名main--Person{name='孙七', age=16}
线程名ForkJoinPool.commonPool-worker-11--Person{name='李四', age=29}
线程名ForkJoinPool.commonPool-worker-9--Person{name='张三', age=24}
线程名ForkJoinPool.commonPool-worker-2--Person{name='陈二', age=12}
线程名ForkJoinPool.commonPool-worker-9--Person{name='刘一', age=25}
sequential()把并行流切换成顺序流,输出:
线程名main--Person{name='刘一', age=25}
线程名main--Person{name='陈二', age=12}
线程名main--Person{name='张三', age=24}
线程名main--Person{name='李四', age=29}
线程名main--Person{name='王五', age=22}
线程名main--Person{name='赵六', age=15}
线程名main--Person{name='孙七', age=16}
线程名main--Person{name='周八', age=18}
1.3 通过Stream的静态方法创建Stream
Stream接口提供了几个静态方法可以创建一个Stream实例,常用的是:of()、iterate()、generate()
/**
* 通过Stream的静态方法创建Stream
*/
@Test
public void testCreateByStaticMethod() {
// 将参数数组作为数据源,返回一个顺序流,底层调用的是Arrays.stream(T[] array)
Stream<Integer> of = Stream.of(0, 1, 5, 2, 4, 8, 9);// 不断产生 0,2,4,6,8,10.,...,n,n+2
System.out.println("of() 将参数数组作为数据源,返回一个顺序流: ");
of.forEach(item -> System.out.print(item + " "));
// 创建无限流,入参是初始元素和UnaryOperator函数式接口,返回一个有规律的无限顺序流
Stream<Integer> iterate = Stream.iterate(0, n -> n + 2);// 不断产生 0,2,4,6,8,10.,...,n,n+2
System.out.println("\n\niterate() 返回一个有规律的无限顺序流,限制输出前10个: ");
iterate.limit(10).forEach(item -> System.out.print(item + " "));
// 创建无限流,入参是Supplier,返回一个无规律的无限顺序流,其中每个元素由提供的Supplier生成,适用于生成恒定流、随机元素流
Stream<Integer> generate = Stream.generate(() -> (int) (Math.random() * 10));// 不断产生 0-9 的随机数
System.out.println("\n\ngenerate() 返回一个有规律的无限顺序流,限制输出前10个: ");
generate.limit(10).forEach(item -> System.out.print(item + " "));
}
运行结果:
of() 将参数数组作为数据源,返回一个顺序流:
0 1 5 2 4 8 9
iterate() 返回一个有规律的无限顺序流,限制输出前10个:
0 2 4 6 8 10 12 14 16 18
generate() 返回一个有规律的无限顺序流,限制输出前10个:
2 6 9 2 7 1 4 6 0 0
2 终止操作
因为 Stream 有惰性求值的特点,中间操作,并不会立即执行,只有触发终止操作时,才会进行实际的运算。
Stream 操作可以有 0个或者多个中间操作
,但是 有且只有一个终止操作
。
所以下面为了方便演示,我们讲解终止操作的使用,再讲解中间操作的使用。
2.1 allMatch
allMatch操作,如果流中所有元素都匹配 predicate 或者是空流,返回true,否则返回false
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* allMatch操作,如果流中所有元素都匹配 predicate 或者是空流,返回true,否则返回false
*/
@Test
public void testStreamAllMatch() {
Stream<Person> emptyStream = Stream.empty();
Stream<Person> stream = persons.stream();
// 流中所有Person元素都大于20岁
boolean allMathchGt20 = stream.allMatch(item -> item.getAge() > 20);
System.out.println("流中所有Person元素都大于20岁: " + allMathchGt20);
// 空流中所有Person元素都大于20岁
boolean allMathchGt20InEmptyStream = emptyStream.allMatch(item -> item.getAge() > 20);
System.out.println("空流中所有Person元素都大于20岁: " + allMathchGt20InEmptyStream);
}
}
运行结果:
流中所有Person元素都大于20岁: false
空流中所有Person元素都大于20岁: true
2.2 noneMatch
noneMatch 操作,如果流中没有元素匹配 predicate 或者是空流,返回true,否则返回false
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* noneMatch 操作,如果流中没有元素匹配 predicate 或者是空流,返回true,否则返回false
*/
@Test
public void testStreamNoneMatch() {
Stream<Person> emptyStream = Stream.empty();
Stream<Person> stream = persons.stream();
// 流中没有Person元素大于30岁
boolean noneMathchGt30 = stream.noneMatch(item -> item.getAge() > 30);
System.out.println("流中没有Person元素大于30岁: " + noneMathchGt30);
// 空流中所有Person元素都大于30岁
boolean noneMathchGt30InEmptyStream = emptyStream.noneMatch(item -> item.getAge() > 30);
System.out.println("空流中没有Person元素大于30岁: " + noneMathchGt30InEmptyStream);
}
}
运行结果:
流中没有Person元素大于30岁: true
空流中没有Person元素大于30岁: true
2.3 anyMatch
anyMatch 操作,如果流中任意一个元素匹配 predicate返回true,否则返回false,空流返回false
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* anyMatch 操作,如果流中任意一个元素匹配 predicate返回true,否则返回false,空流返回false
*/
@Test
public void testStreamAnyMatch() {
Stream<Person> emptyStream = Stream.empty();
Stream<Person> stream = persons.stream();
// 流中任意一个Person元素都小于18岁
boolean anyMathchGt18 = stream.anyMatch(item -> item.getAge() > 18);
System.out.println("流中任意一个Person元素都小于18岁: " + anyMathchGt18);
// 空流中任意一个Person元素都小于18岁
boolean anyMathchGt18InEmptyStream = emptyStream.anyMatch(item -> item.getAge() > 18);
System.out.println("空流中任意一个Person元素都小于18岁: " + anyMathchGt18InEmptyStream);
}
}
运行结果:
流中任意一个Person元素都小于18岁: true
空流中任意一个Person元素都小于18岁: false
2.4 collect
collect操作:接收一个
Collector
实例,将流中元素收集成另外一个数据结构
2.4.1 介绍 Collector 和 Collectors
Collector 作为 Stream 的collect操作的参数类型,能够把流中的元素序列收集到另一个数据结构中。
Collectors是工具类,提供了很多静态方法用于生成Collector实例
Collectors常用API
静态方法 | 描述 |
---|---|
Collector<T, ?, List> toList() | 将输入的元素,收集到一个List容器中 |
Collector<T, ?, Set> toSet() | 将输入的元素,收集到一个Set容器中 |
Collector<T, ?, Map<K,U>> toMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) |
第一个参数keyMapper:获取map的key的Function 第二个参数valueMapper:获取map的value的Function 将输入的元素,收集到一个Map<K,U>>容器中 |
Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) |
第一个参数keyMapper:获取map的key的Function 第二个参数valueMapper:获取map的value的Function 将输入的元素,收集到一个ConcurrentMap<K,U>容器 |
2.4.2 collect操作测试用例
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* collect操作:接收一个Collector实例,将流中元素收集成另外一个数据结构
*/
@Test
public void testStreamCollect() {
Stream<Person> stream2List = persons.stream();
// 将流中元素收集成 List
List<Person> list = stream2List.collect(Collectors.toList());
System.out.println("将流中元素收集成 List :\n" + list);
Stream<Person> stream2Map = persons.stream();
// 将流中元素收集成 map,key: name,value: person
// Function,通过输入的Person,获取name作为map的key
Function<Person, String> nameFunc = item -> item.getName();
// Function,通过输入的Person,获取 person 作为map的value
Function<Person, Person> personFunc = item -> item;
Map<String, Person> map = stream2Map.collect(Collectors.toMap(nameFunc, personFunc));
System.out.println("\n将流中元素收集成 map,key: name,value: person :\n" + map);
}
}
运行结果:
将流中元素收集成 List :
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
将流中元素收集成 map,key: name,value: person :
{孙七=Person{name='孙七', age=16}, 李四=Person{name='李四', age=29}, 张三=Person{name='张三', age=24}, 刘一=Person{name='刘一', age=25}, 陈二=Person{name='陈二', age=12}, 王五=Person{name='王五', age=22}, 周八=Person{name='周八', age=18}, 赵六=Person{name='赵六', age=15}}
2.5 count
count操作:返回到达终止操作时,流中元素总数
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* count操作:返回到达终止操作时,流中元素总数
*/
@Test
public void testStreamCount() {
Stream<Person> stream = persons.stream();
// 流中元素总数
long count = stream.count();
System.out.println("流中元素总数: " + count);
}
}
运行结果:
流中元素总数: 8
2.6 findAny
findAny 操作:返回任意一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* findAny 操作:返回任意一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
*/
@Test
public void testStreamFindAny() {
Stream<Person> stream = persons.stream();
// 非空流:返回任意一个元素
Optional<Person> any = stream.findAny();
System.out.println("非空流:返回任意一个元素: " + any);
Stream<Person> emptyStream = Stream.empty();
// 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
Optional<Person> findAnyEmpty = emptyStream.findAny();
System.out.println("空流:取任意一个元素时返回空的Optional: " + findAnyEmpty);
}
}
运行结果:
非空流:返回任意一个元素: Optional[Person{name='刘一', age=25}]
空流:取任意一个元素时返回空的Optional: Optional.empty
2.7 findFirst
findFirst操作:返回流中第一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* findFirst操作:返回流中第一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
*/
@Test
public void testStreamFindFirst() {
Stream<Person> stream = persons.stream();
// 非空流:返回流中第一个元素
Optional<Person> first = stream.findFirst();
System.out.println("非空流:返回流中第一个元素: " + first);
Stream<Person> emptyStream = Stream.empty();
// 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
Optional<Person> findFirstEmpty = emptyStream.findFirst();
System.out.println("空流:取第一个第一个元素时返回空的Optional: " + findFirstEmpty);
}
}
运行结果:
非空流:返回流中第一个元素: Optional[Person{name='刘一', age=25}]
空流:取第一个第一个元素时返回空的Optional: Optional.empty
2.8 forEach
forEach操作:对此流的每个元素执行 Consumer 操作。
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* forEach操作:对此流的每个元素执行 Consumer 操作
*/
@Test
public void testStreamForEach() {
Stream<Person> stream = persons.stream();
//forEach操作:对此流的每个元素执行 Consumer 操作
// 打印流中的每一个元素
System.out.println("打印流中的每一个元素:");
stream.forEach(item -> System.out.println(item));
Stream<Person> stream2 = persons.stream();
// 把流中的每一个元素收集进list
List<Person> list = new ArrayList<>();
stream2.forEach(list::add);
System.out.println("\n把流中的每一个元素收集进list:");
System.out.println(list);
}
}
运行结果:
打印流中的每一个元素:
Person{name='刘一', age=25}
Person{name='陈二', age=12}
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}
把流中的每一个元素收集进list:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
2.9 max
max 操作:根据给定的Comparator,返回流中最大的元素,用Optional描述,
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* max 操作:根据给定的Comparator,返回流中最大的元素,用Optional描述,
* 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
*/
@Test
public void testStreamMax() {
Stream<Person> stream = persons.stream();
// 返回流中年龄最大的元素
// Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
// stream.max(Comparator.comparingInt(person -> person.getAge()));
// person的age属性比较器
Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
Optional<Person> max = stream.max(ageComparator);
System.out.println("返回流中最大的元素:" + max);
Stream<Person> emptyStream = Stream.empty();
// 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
Optional<Person> maxInEmpty = emptyStream.max(ageComparator);
System.out.println("空流:取最大元素时返回空的Optional: " + maxInEmpty);
}
}
运行结果:
返回流中最大的元素:Optional[Person{name='李四', age=29}]
空流:取最大元素时返回空的Optional: Optional.empty
2.10 min
min 操作:根据给定的Comparator,返回流中最小的元素,用Optional描述
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* min操作:根据给定的Comparator,返回流中最小的元素,用Optional描述,
* 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
*/
@Test
public void testStreamMin() {
Stream<Person> stream = persons.stream();
// 返回流中年龄最小的元素
// Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
// stream.min(Comparator.comparingInt(person -> person.getAge()));
// person的age属性比较器
Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
Optional<Person> min = stream.min(ageComparator);
System.out.println("返回流中最小的元素:" + min);
Stream<Person> emptyStream = Stream.empty();
// 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
Optional<Person> minInEmpty = emptyStream.min(ageComparator);
System.out.println("空流:去最小元素时返回空的Optional: " + minInEmpty);
}
}
运行结果:
返回流中最小的元素:Optional[Person{name='陈二', age=12}]
空流:去最小元素时返回空的Optional: Optional.empty
2.11 Optional reduce(BinaryOperator accumulator)
Optional reduce(BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;
依次类推。最终得到用Optional描述的T类型的结果。
/**
* Optional reduce(BinaryOperator accumulator):
* 第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;
* 第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;
* 依次类推。最终得到用Optional描述的T类型的结果。
*/
@Test
public void testStreamReduce1() {
// 计算 nums 数组中所有元素的和
int[] nums = new int[]{1, 5, 2, 4, 8, 9};
// Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
IntStream stream = Arrays.stream(nums);
// (n1, n2) -> n1 + n2 二元运算 lambda 表达式,用于返回两个数值 n1 + n2 的结果
OptionalInt reduce = stream.reduce((n1, n2) -> n1 + n2);
System.out.println("计算 nums 数组中素有元素的和: " + reduce.getAsInt());
}
运行结果:
计算 nums 数组中素有元素的和: 29
2.12 T reduce(T identity, BinaryOperator accumulator)
T reduce(T identity, BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数identity,第二个参数为流中元素的第一个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第二个元素;
依次类推。最终得到T类型的结果。
/**
* T reduce(T identity, BinaryOperator accumulator):
* 第一次执行时,accumulator函数的第一个参数identity,第二个参数为流中元素的第一个元素;
* 第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第二个元素;
* 依次类推。最终得到T类型的结果。
*/
@Test
public void testStreamReduce2() {
// strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀
String[] strs = new String[]{"hello", "Stream", "终止操作", "reduce"};
// Arrays.stream(strs) 将指定的数组作为数据源,返回一个顺序流
Stream<String> stream = Arrays.stream(strs);
// (n1, n2) -> n1 + n2 二元运算 lambda 表达式,把 str1 和 str2 两个字符串用 " " 空格连接
BinaryOperator<String> binaryOperator = (str1, str2) -> {
StringBuilder sb = new StringBuilder();
sb.append(str1).append(" ").append(str2);
return sb.toString();
};
String reduce = stream.reduce("黑桃", binaryOperator);
System.out.println("strs 数组中所有元素用空格连接,并加上 \"黑桃\" 字符串作为前缀:\n " + reduce);
}
运行结果:
strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀:
黑桃 hello Stream 终止操作 reduce
2.13 toArray
Object[] toArray() 操作:返回包含此流元素的数组.
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* Object[] toArray() 操作:返回包含此流元素的数组
*/
@Test
public void testToArray() {
Stream<Person> stream = persons.stream();
// 返回包含此流元素的数组
Object[] objects = stream.toArray();
System.out.println(Arrays.asList(objects));
}
}
运行结果:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
3 中间操作
中间操作可以像数据库操作一样,让我们可以对数据源进行一些聚合操作,比如 distinct、limit…
**再次提示:**Stream 操作可以有 0个或者多个中间操作
,但是 有且只有一个终止操作
。
这里我们先演示每个中间操作的单独使用,最后再演示多个中间操作串联的测试用例。
3.1 distinct
distinct 操作:返回由不同元素组成的流,根据元素的
equals()
方法去重
public class Person {
//姓名
private String name;
//年龄
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return getAge() == person.getAge() &&
Objects.equals(getName(), person.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
}
/**
* distinct 操作:返回由不同元素组成的流,根据元素的equals()方法去重
*/
@Test
public void testDistinct() {
Person[] personArray = new Person[]{
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("张三", 24),
new Person("王五", 22),
new Person("赵六", 15)
};
Stream<Person> stream = Arrays.stream(personArray);
System.out.println("根据元素的equals()方法去重:");
stream.distinct().forEach(item -> System.out.println(item));
}
运行结果:
根据元素的equals()方法去重:
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
3.2 filter
filter 操作:过滤,排除掉没有匹配 predicate 函数式接口的元素
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* filter 操作:过滤,排除掉没有匹配 predicate 函数式接口的元素
*/
@Test
public void testFilter() {
//过滤,返回年龄大于25岁的Person
System.out.println("返回年龄大于25岁的Person:");
// Predicate 函数式接口的 lambda表达式,判断传入的 person 的年龄是否大于25岁
Predicate<Person> ageGt25Predicate = person -> person.getAge() > 25;
persons.stream()
.filter(ageGt25Predicate)
.forEach(item -> System.out.println(item));
}
}
运行结果:
返回年龄大于25岁的Person:
Person{name='李四', age=29}
3.3 limit
limit 操作:限制流的元素数量,截断流,使其元素不超过给定数量
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* limit 操作:限制流的元素数量,截断流,使其元素不超过给定数量
*/
@Test
public void testlimit() {
System.out.println("persons 列表:\n" + persons);
// 返回 persons 列表中前3个元素
List<Person> collect = persons.stream()
.limit(3)//限制流中的最大元素数量为3
.collect(Collectors.toList());//将流中元素收集到list中
System.out.println("\n返回 persons 列表中前3个元素:\n" + collect);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
返回 persons 列表中前3个元素:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}]
3.4 skip
skip 操作:跳过元素,返回一个扔掉了前 n 个元素的流。如果此流包含的元素少于n,则返回空流
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* skip 操作:跳过元素,返回一个扔掉了前 n 个元素的流。如果此流包含的元素少于n,则返回空流
*/
@Test
public void testSkip() {
System.out.println("persons 列表:\n" + persons);
// 跳过 persons 列表中前3个元素
List<Person> collect = persons.stream()
.skip(3)//跳过流中的前3个元素
.collect(Collectors.toList());//将流中元素收集到list中
System.out.println("\n跳过 persons 列表中前3个元素:\n" + collect);
// 如果此流包含的元素少于n,则返回空流
List<Person> empty = persons.stream()
//跳过流中的前10个元素,流包含的元素少于10,返回空流
.skip(10)
.collect(Collectors.toList());//将流中元素收集到list中
System.out.println("\n跳过 persons 列表中前10个元素:\n" + empty);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
跳过 persons 列表中前3个元素:
[Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
跳过 persons 列表中前10个元素:
[]
3.5 sorted()
sorted 操作:自然排序后返回一个新的流,如果元素不是Comparable类型,则抛出ClassCastException
/**
* sorted 操作:自然排序后返回一个新的流,如果元素不是Comparable类型,则抛出ClassCastException
*/
@Test
public void testSorted() {
int[] nums = new int[]{1, 5, 2, 4, 8, 9};
System.out.println("对数组进行自然排序:");
Arrays.stream(nums).sorted().forEach(System.out::println);
}
运行结果:
对数组进行自然排序:
1
2
4
5
8
9
3.6 Stream sorted(Comparator<? super T> comparator)
Stream sorted(Comparator<? super T> comparator) 操作:根据给定 Comparator 排序后,返回一个新流。
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* Stream sorted(Comparator<? super T> comparator) 操作:根据给定 Comparator 排序后,返回一个新流
*/
@Test
public void testSorted2() {
System.out.println("persons 列表:\n" + persons);
// person的age属性比较器
Comparator<Person> ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
System.out.println("按照 age 升序排序:");
persons.stream()
// 根据给定 Comparator 排序
.sorted(ageComparator)
.forEach(System.out::println);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
按照 age 升序排序:
Person{name='陈二', age=12}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}
Person{name='王五', age=22}
Person{name='张三', age=24}
Person{name='刘一', age=25}
Person{name='李四', age=29}
3.7 map
map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
*/
@Test
public void testMap() {
System.out.println("persons 列表:\n" + persons);
//获取 persons 中每个元素的 name 属性存放到 List<String> 中
List<String> list = persons.stream()
//map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
// Function函数式接口,获取入参person中的 name 属性
// 将 Person 类型元素映射成String类型的新元素
.map(person -> person.getName())
// 将流中元素收集成 List
.collect(Collectors.toList());
System.out.println("获取 persons 中每个元素的 name 属性存放到 List<String> 中:\n" + list);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 name 属性存放到 List<String> 中:
[刘一, 陈二, 张三, 李四, 王五, 赵六, 孙七, 周八]
3.8 mapToInt、mapToLong、mapToDouble
mapToInt 操作:通过入参ToIntFunction函数式接口将流中的每个T类型元素映射成一个Integer类型的新元素
mapToLong 操作:通过入参ToLongFunction函数式接口将流中的每个T类型元素映射成一个Long类型的新元素
mapToDouble 操作:通过入参ToDoubleFunction函数式接口将流中的每个T类型元素映射成一个Double类型的新元素
mapToInt简单使用
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* mapToInt 操作:通过入参ToIntFunction函数式接口将流中的每个T类型元素映射成一个Integer类型的新元素
*/
@Test
public void testMapToInt() {
System.out.println("persons 列表:\n" + persons);
//获取 persons 中每个元素的 age 属性存放到 List<Integer> 中
List<Integer> list = new ArrayList<>();
persons.stream()
//map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
// Function函数式接口,获取入参person中的 age 属性
// 将 Person 类型元素映射成 int 类型的新元素
.mapToInt(person -> person.getAge())
// 将流中元素收集成 List
.forEach(list::add);
System.out.println("获取 persons 中每个元素的 age 属性存放到 List<Integer> 中:\n" + list);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 age 属性存放到 List<Integer> 中:
[25, 12, 24, 29, 22, 15, 16, 18]
3.9 peek
peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作,如给元素属性赋值,每个元素引用没变
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作,如给元素属性赋值,每个元素引用没变
*/
@Test
public void testPeek() {
System.out.println("persons 列表:\n" + persons);
System.out.println("\n给 persons 中每个元素的 name 属性添加\"_peek\"后缀:");
//给 persons 中每个元素的 name 属性添加"_peek"后缀
persons.stream()
//peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作
// Consumer函数式接口,将入参person中的 name 属性添加"_peek"后缀
// 将 Person 类型元素映射成String类型的新元素
.peek(person -> {
String name = person.getName();
person.setName(name + "_peek");
})
.forEach(System.out::println);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
给 persons 中每个元素的 name 属性添加"_peek"后缀:
Person{name='刘一_peek', age=25}
Person{name='陈二_peek', age=12}
Person{name='张三_peek', age=24}
Person{name='李四_peek', age=29}
Person{name='王五_peek', age=22}
Person{name='赵六_peek', age=15}
Person{name='孙七_peek', age=16}
Person{name='周八_peek', age=18}
3.10 flatMap
flatMap 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个流,然后把转换后的所有流连接成一个流
/**
* flatMap 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个流,然后把转换后的所有流连接成一个流
*/
@Test
public void testFlatMap() {
Person[] array1 = new Person[]{
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
};
Person[] array2 = new Person[]{
new Person("刘一", 25),
new Person("陈二", 12),
};
Person[] array3 = new Person[]{
new Person("孙七", 16),
new Person("周八", 18)
};
// 将 array1、array2、array3 3个person数组的所有Person元素收集存放到 List<Person> 中
List<Person> list = Stream.of(array1, array2, array3)
//flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
// 然后把转换后的所有流连接成一个流
.flatMap(array -> Arrays.stream(array))
// 将流中元素收集成 List
.collect(Collectors.toList());
System.out.println("将 array1、array2、array3 3个person数组的所有Person元素收集存放到 List<Person> 中:\n" + list);
}
运行结果:
将 array1、array2、array3 3个person数组的所有Person元素收集存放到 List<Person> 中:
[Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
3.11 flatMapToInt、flatMapToLong、flatMapToDouble
flatMapToIn 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个IntStream(流中元素的类型是Integer),然后把转换后的所有IntStream连接成一个IntStream
flatMapToLong 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个LongStream(流中元素的类型是Long),然后把转换后的所有LongStream连接成一个LongStream
flatMapToDouble 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个DoubleStream(流中元素的类型是Double),然后把转换后的所有DoubleStream连接成一个DoubleStream
flatMapToInt 简单使用
/**
* flatMapToInt 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个IntStream(流中元素的类型是Integer),
* 然后把转换后的所有IntStream连接成一个IntStream
*/
@Test
public void testFlatMapToInt() {
Person[] array1 = new Person[]{
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
};
Person[] array2 = new Person[]{
new Person("刘一", 25),
new Person("陈二", 12),
};
Person[] array3 = new Person[]{
new Person("孙七", 16),
new Person("周八", 18)
};
// 将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:
List<Integer> list = new ArrayList<>();
Stream.of(array1, array2, array3)
//flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
// 然后把转换后的所有流连接成一个流
.flatMapToInt(array -> {
int[] ages = new int[array.length];
for (int i = 0; i < array.length; i++) {
ages[i] = array[i].getAge();
}
return IntStream.of(ages);
})
// 将流中元素收集成 List
.forEach(item -> list.add(item));
System.out.println("将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:\n" + list);
}
运行结果:
将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List<Integer> 中:
[24, 29, 22, 25, 12, 16, 18]
3.12 多个中间操作连接
public class StreamAPITest {
List<Person> persons = null;
@Before
public void before() {
persons = Arrays.asList(
new Person("刘一", 25),
new Person("陈二", 12),
new Person("张三", 24),
new Person("李四", 29),
new Person("王五", 22),
new Person("赵六", 15),
new Person("孙七", 16),
new Person("周八", 18)
);
}
/**
* 测试多个中间操作:Stream操作中支持 0个或者多个 中间操作
*/
@Test
public void testMultiOpr() {
System.out.println("persons 列表:\n" + persons);
//将 persons 中 age 大于18 的前两个person的name 收集到 List<String> 中
List<String> collect = persons.stream()
//过滤出年龄大于18的元素
.filter(person -> person.getAge() > 18)
//限制返回前两个元素
.limit(2)
// 映射:返回person的name属性
.map(person -> person.getName())
// 将流中的元素收集到 list 中
.collect(Collectors.toList());
System.out.println("\n将 persons 中 age 大于18 的前两个person的name 收集到 List<String> 中:");
System.out.println(collect);
}
}
运行结果:
persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
将 persons 中 age 大于18 的前两个person的name 收集到 List<String> 中:
[刘一, 张三]
Stream 提供了大量的对集合等数据源的增强操作,上面给大家演示的是工作中比较常用的创建、中间操作、终止操作的API使用。
Stream+Lambda表达式 让我们对集合等数据源的操作更加简洁清晰,便于代码的维护。希望大家学以致用,熟能生巧。
共同学习,写下你的评论
评论加载中...
作者其他优质文章