目录
1. 流式思想概述
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序把原材料加工成一个商品。
Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,提供了一种高效且易于使用的处理数据的方式。
这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而最右侧的数字3是最终结果。
Stream API是用来处理集合、数组等容器中的数据的,处理操作有:查询、筛选、删除、过滤、统计、映射等。集合、数组讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据。
Stream的执行流程:
1、获取Stream流:通过一个数据源(如:集合、数组),获取一个流
2、中间处理操作:中间操作是个操作链,对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行
3、终结操作:一旦执行终结操作,就执行中间操作链,最终产生结果并结束Stream。终结操作执行后就不能再对数据进行加工处理,如果要加工需要重新创建Stream。
Stream流的特点:
(1)Stream流本身不负责存储数据,存储数据是用集合、数组等数据结构。
(2)Stream流不会修改数据源的数据,每次处理都会返回一个持有结果的新Stream流对象。
(3)Stream流的操作是一个延迟操作,不调用终结方法 中间处理操作不会执行
注意:Stream流和IO(InputStream/OutputStream)流没有任何关系。
2. 获取stream流的方式
java.util.stream.Stream<T>
是Java 8新增的stream流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:
Collection
接口中的默认方法default Stream<E> stream()
,所有的Collection集合都可以通过该方法获取流对象- 通过数组工具类
Arrays
类的静态方法public static <T> Stream<T> stream(T[] array)
获取数组对应的流对象。 Stream
接口的静态方法public static<T> Stream<T> of(T... values)
获取数组对应的流对象
根据Collection集合获取流
Collection
接口的所有实现类均可通过stream
方法获取流。
@Test
public void test1() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
Stream<Integer> stream = list.stream();
}
根据数组获取流
@Test
public void test2() {
String[] array = {"aaa", "bbb", "ccc", "ddd"};
Stream<String> stream1 = Arrays.stream(array);
Integer[] array2 = {1, 4, 3, 10, 12, 21};
Stream<Integer> stream2 = Arrays.stream(array2);
}
@Test
public void test3() {
String[] array = {"aaa", "bbb", "ccc", "ddd"};
Stream<String> stream1 = Stream.of(array);
/*int[] array2 = {1, 4, 3, 10, 12, 21};
Stream<int[]> stream2 = Stream.of(array2);*/
Integer[] array2 = {1, 4, 3, 10, 12, 21};
Stream<Integer> stream2 = Stream.of(array2);
}
3. 常用方法:
Stream API 分为两类:终结方法和非终结方法。
- 非终结方法:返回值类型仍然是
Stream
接口类型的方法,因此支持链式调用 - 终结方法:返回值类型不再是
Stream
接口类型的方法,因此不再支持链式调用
注意:
- 非终结方法是延迟执行的,调用终结方法时,才会真正执行前面的非终结方法
- Stream API 仅对Stream流中的数据进行加工处理,不会修改数据源的数据。
forEach : 逐一处理
终结方法,调用该方法后不能调用其他方法,方法声明:
void forEach(Consumer<? super T> action);
该方法接收一个Consumer
接口对象的参数,会对每一个元素进行逐一处理,例如遍历:
@Test
public void test1() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
Stream<Integer> stream = list.stream();
//可传入lambda表达式、方法引用:Consumer<Integer> consumer = System.out::println;
stream.forEach(System.out::println);
}
count:统计流中的元素个数
终结方法,方法声明:
long count();
代码示例
@Test
public void test2() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
Stream<Integer> stream = list.stream();
long count = stream.count();
System.out.println(count); //6
}
filter:过滤
非终结方法,方法声明:
Stream<T> filter(Predicate<? super T> predicate);
该方法接收一个Predicate
函数式接口对象的参数作为筛选条件
筛选出集合中的偶数,代码示例:
@Test
public void test3() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
//链式调用
long count = list.stream().filter(i -> i % 2 == 0).count();
System.out.println(count);
}
limit:取出前n个元素
非终结方法,方法声明:
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
代码示例:
@Test
public void test4() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
list.stream().limit(4).forEach(System.out::println);
}
skip:跳过前n个元素
非终结方法,方法声明:
Stream<T> skip(long n):
跳过前n个元素,获取一个截取之后的新流,如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流。
代码示例:
@Test
public void test5() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
list.stream().skip(3).forEach(System.out::println); //10 12 21
}
sorted:排序
自然排序:
Stream<T> sorted();
代码示例:
@Test
public void test6() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
list.stream().sorted().forEach(System.out::println);
}
定制排序:
Stream<T> sorted(Comparator<? super T> comparator);
@Test
public void test6() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
list.stream().sorted((i1, i2) -> -Integer.compare(i1, i2)).forEach(System.out::println);
}
map:映射
非终结方法,方法声明:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
将流中的元素映射到另一个流中
代码示例:
@Test
public void test7() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
//Function<Integer, String> mapper = i -> i + "a";
list.stream().map(i -> i + "a").forEach(System.out::println); //1a 4a 3a 10a 12a 21a
}
distinct:去除重复数据
非终结方法,方法声明:
Stream<T> distinct();
代码示例:
@Test
public void test8() {
List<Integer> list = Arrays.asList(4, 1, 4, 3, 10, 3, 12, 21, 3);
list.stream().distinct().forEach(System.out::println);
}
自定义类型是根据对象的hashCode和equals来去除重复元素的:
public class Student {
private String name;
private int age;
//构造器、hashcode、equals等
}
@Test
public void test9() {
Stream.of(new Student("jack", 12), new Student("lily", 17)
, new Student("mark", 17), new Student("jack", 12)
, new Student("lily", 15), new Student("john", 16))
.distinct().forEach(System.out::println);
}
reduce:归约
将流中元素反复结合起来,得到一个值
终结方法,方法声明:
T reduce(T identity, BinaryOperator<T> accumulator);
@Test
public void test11() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
//reduce:
//第一次,将初始值赋值给i1,取出第一个元素赋值给i2
//第二次,将第一次的结果赋值给i1,取出二个元素赋值给i2
//第三次,将第二次的结果赋值给i1,取出三个元素赋值给i2
//第四次,将第三次的结果赋值给i1,取出四个元素赋值给i2
System.out.println(list.stream().reduce(5, (i1, i2) -> i1 + i2)); //56
}
Optional<T> reduce(BinaryOperator<T> accumulator);
@Test
public void test12() {
List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
Optional<Integer> sum = list.stream().reduce((i1, i2) -> i1 + i2);
System.out.println(sum.get()); //51
}
concat:组合
非终结方法,将两个Stream流对象合并成一个新的Stream流对象,该方法为静态方法,方法声明:
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
代码示例:
@Test
public void test13() {
Stream<String> streamA = Stream.of("ccc", "abc", "bbb");
Stream<String> streamB = Stream.of("aaa", "eee", "ddd");
Stream.concat(streamA, streamB).forEach(System.out::println);
}
collect:收集Stream流中的结果到指定结构
Stream流中的结果收集到集合中
java.util.stream.Collector<T, A, R>
接口对象来指定收集到哪种集合中,java.util.stream.Collectors
类提供一些方法来返回 Collector
接口的对象。
@Test
public void test14() {
//转为list集合
List<String> list = Stream.of("ccc", "abc", "bbb")
.collect(Collectors.toList());
System.out.println(list);
ArrayList<String> arrayList = Stream.of("ccc", "abc", "bbb")
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(arrayList);
//转为set集合
Set<String> set = Stream.of("aaa", "eee", "ddd")
.collect(Collectors.toSet());
System.out.println(set);
HashSet<String> hashSet = Stream.of("aaa", "eee", "ddd")
.collect(Collectors.toCollection(HashSet::new));
System.out.println(hashSet);
}
Stream流中的结果收集到数组中
Stream流提供toArray
方法将结果收集到一个数组中,返回值类型为Object[],方法声明:
Object[] toArray();
@Test
public void test15() {
Object[] array = Stream.of("ccc", "abc", "bbb")
.toArray();
System.out.println(Arrays.toString(array)); //[ccc, abc, bbb]
}