概要
首先要澄清的是 java8 中的stream 与InputStream和OutputStream是完全不同的概念, stream 是用于对集合迭代器的增强,使之完成 能够完成更高效的聚合操作(过滤、排序、统计分组)或者大批量数据操作。此外与stream 与lambda 表达示结合后编码效率与大大提高,并且可读性更强。
应用场景:数据源从多个地方汇集而来(必须从多个数据库中查到的数据)
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class AppleStore {
private Integer id;
private String color;
private Integer weight;
}
示例展示1:
// 获取所有红色苹果的总重量
List<AppleStore> appleStoreList = Arrays.asList(
new AppleStore(1, "red", "10"),
new AppleStore(2, "blue", "11"),
new AppleStore(3, "blue", "13"),
new AppleStore(4, "red", "11")
);
int sums = appleStoreList
.stream()
.filter(x -> "red".equals(x.getColor()))
.mapToInt(n -> Integer.parseInt(n.getWeight()))
.sum();
System.out.println(sums);
示例展示2:
基于水果的颜色,求出水果平均重量;
// 基于颜色统计平均重量
List<AppleStore> appleStoreList = Arrays.asList(
new AppleStore(1, "red", "10"),
new AppleStore(2, "blue", "11"),
new AppleStore(3, "blue", "13"),
new AppleStore(4, "red", "11")
);
Map<String, Double> map = appleStoreList.stream()
.collect(Collectors.groupingBy(
x -> x.getColor(),
Collectors.averagingInt(x -> Integer.parseInt(x.getWeight()))
));
System.out.println(JSON.toJSONString(map));
stream 产生背景
‘获取所有红色苹果的总重量’,如果用SQL其实非常好实现,为什么不在直接关系数据库来实现呢?
//获取所有红色苹果的总重量
select sum(a.weight) from apple as a where a.color='red';
// 基于颜色分组统计重量
select a.color,sum(a.weight) from apple as a group by color;
遍历在传统的javaEE 项目中数据源比较单一而且集中,像这类的需求都我们可能通过关系数据库中进行获取计算。但现在的互联网项目数据源成多样化有:关系数据库、NoSQL、Redis、mongodb、ElasticSearch、Cloud Server 等。这时就需我们从各数据源中汇聚数据并进行统计。这在Stream出现之前只能过遍历实现 非常繁琐。
创建流方式:
1、Stream.of(T... val) 参数是多参泛型
Stream s1 = Stream.of("1", "2", "3");
2、Arrays.stream(T[]) 参数是数组
IntStream s2 = Arrays.stream(new int[]{1, 2, 3});
3、集合、 列表 .stream()
List<Integer> r = new ArrayList<>();
Stream s = r.stream();
流的执行过程
流组成
1、创建流.中间节点.中间节点.··· ···.终止节点;
list.stream().filter().limit().toArray()
中间节点:一般返回的是一个流,后边可以继续调用;
终止节点:返回的不是一个流,一般返回一个结果集。
Integer[] r = {1, 2, 3};
Object[] s = Arrays.stream(r).limit(2).toArray();
List<AAA> a = Arrays.asList(
new AAA("1", 20),
new AAA("2", 22),
new AAA("3", 23),
new AAA("4", 24)
);
Object res = a.stream().filter(x -> x.getAge() > 22).toArray();
中间节点的影响
(1)过滤
经过filter过滤函数之后,后边只能操作过滤之后的数组了。
Integer[] r = {1, 2, 3, 4, 5};
Object[] s = Arrays.stream(r).filter(x -> x >= 3).limit(2).toArray();
System.out.println(JSON.toJSONString(s));
(2)转换
经过map函数会把集合中的每一个AAA转化为以 年龄组成的集合。
List<AAA> a = Arrays.asList(
new AAA("1", 20),
new AAA("2", 22),
new AAA("3", 23),
new AAA("4", 24)
);
Object res = a.stream().filter(x -> x.getAge() >= 22)
.map(x -> x.getAge())
.toArray();
System.out.println(JSON.toJSONString(res));
(3)去重
distinct() 去重
Integer[] i = new Integer[]{1, 1, 2, 2, 3, 4, 3, 2};
Object[] a = Arrays.stream(i).filter(x -> x >= 2).distinct().toArray();
System.out.println(JSON.toJSONString(a));
stream流的debugger模式
这个要靠idea了,查看当前流的链。
很清晰,每一个过滤函数执行、去重、之后的结果,执行:
中间节点方法有哪些
除了上边的filter 、map、distinct、sorted()排序
方法 | 描述 | 操作类型 |
filter | 接收一个Boolean表达示来过滤元素 | 中间操作 |
map | 将流中元素 1:1 映谢成另外一个元素 | 中间操作 |
mapToInt | 将流中元素映谢成int,mapToLong、mapToDouble操作类似目的减少 装箱拆箱带来的损耗 | 中间操作 |
flatMap | 如map时返回的是一个List, 将会进一步拆分。详见flatMap示例 | 中间操作 |
forEach | 遍历流中所有元素 | 终值操作 |
sorted | 排序 x,y -> x.getID - y.getID | 中间操作 |
peek | 遍历流中所有元素 ,跟forEach不同在于不会结束流 | 中间操作 |
toArray | 将流中元素转换成一个数组返回 | 终值操作 |
reduce | 归约合并操作 | 中间操作 |
collect | 采集数据,返回一个新的结果 参数说明: Supplier<R>: 采集需要返回的结果 BiConsumer<R, ? super T>:传递结果与元素进行合并。 BiConsumer<R, R>:在并发执行的时候 结果合并操作。详见 collec示例 | 终值操作 |
distinct | 基于equal 表达示去重 | 中间操作 |
max | 通过比较函数 返回最大值 | 终值操作 |
anyMatch | 流中是否有任一元素满足表达示 | 终值操作 |
allMatch | 流中所有元素满足表达示返回true | 终值操作 |
noneMatch | 与allMatch 相反,都不满足的情况下返回 true | 终值操作 |
findFirst | 找出流中第一个元素 | 终值操作 |
of | 生成流 | 生成流操作 |
iterate | 基于迭代生成流 | 生成流操作 |
generate | 基于迭代生成流,与iterate 不同的是不 后一元素的生成,不依懒前一元素 | 生成流操作 |
concat | 合并两个相同类型的类 | 生成流操作 |
1、peek() 执行函数、forEach()执行函数
peek和forEach都是执行函数,不会做任何过滤操作,只是执行。
区别:peek是中间节点,可以往后继续调用,而forEach是终止节点。
Integer[] i = new Integer[]{1, 1, 2, 2, 3, 4, 3, 2};
Arrays.stream(i).filter(x -> x >= 2)
.distinct()
.peek(n -> System.out.println(n)).toArray();
2、collect 采集
很重要
· 把结果转化为一个list
· 把结果转化为map
· 把结果分组
· 转化为数组
· 求最大值
· 求任意值
来看几个
1、把结果转化为 map集合,键为:id,值为:对象
结果转化为map时,如果key有重复的,会报错,当然你可以在加一个策略toMap(k, y, z),参数z可以把重复的去掉。
// Person
public class AAA {
private Integer id;
private String name;
private Integer age;
}
List<Person> a = Arrays.asList(
new Person(1, "1", 20),
new Person(2, "2", 22),
new Person(3, "3", 23),
new Person(4, "4", 24)
);
Map<Integer, Person> res = a.stream()
.filter(x -> x.getAge() >= 22)
// key value
.collect(Collectors.toMap(n -> n.getId(), n -> n));
System.out.println(JSON.toJSONString(res, SerializerFeature.PrettyFormat));
补充下:
内置的四个函数式接口
1、Consumer<T> 消费型接口
无参数,无参返回值。
public static void main(String[] args) {
// Consumer 消费型接口
t(10, System.out::println);
}
static public void t(Integer sum, Consumer<Integer> consumer) {
consumer.accept(sum);
}
2、Supplier<T> 供给型接口
无参数,有返回值。
public static void main(String[] args) {
String r = "abc";
// Supplier 供给型接口
// new Random().nextInt(10) 随机生成[0~10)之间整数
String res = JSON.toJSONString(t(10, () -> new Random().nextInt(10)));
System.out.println(res);
}
static public List<Integer> t(Integer n, Supplier<Integer> supplier) {
List<Integer> list = new ArrayList<>();
for (Integer i = 0; i < n; i++) {
list.add(supplier.get());
}
return list;
}
3、Function<T, V> 函数型接口
有参数,有返回值。
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);}
public static void main(String[] args) {
// Function 函数型接口
System.out.println(t(" a ", x -> x.trim()));
}
static public String t(String str, Function<String, String> function) {
return function.apply(str);
}
4、Predicate<T> 断言型接口
有参,返回布尔值。
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
public static void main(String[] args) {
// Predicate 断言型接口
System.out.println(t("abc", "abc"::equals));
}
static public boolean t(String str, Predicate<String> predicate) {
return predicate.test(str);
}