Java8中的Stream流

今天看到微信公众号上推送了使用Stream流来减小 查询树形结构(因为存parentId,取树时多次查)压力,因此,相对自己代码进行下优化。

使用如下:

建立实体类

public class TaareaTreeVo implements Serializable {
    private static final long serialVersionUID = 5857019442742853202L;
    //行政区划ID
    private String areaid;
    //父级ID
    private String parentid;
    //行政区划名称
    private String areaname;
    //行政区划编码
    private String areacode;
    //行政区划层级
    private Integer arealevel;
    // 子节点数量
    private Integer childNum;
    //子节点
    private List<TaareaTreeVo> children;
//    属性 对应  get/set方法
}
/**
 * 递归查询子节点
 * @param root  根节点
 * @param all   所有节点
 * @return 根节点信息
 */
private List<TaareaTreeVo> getChildrens(TaareaTreeVo root,List<TaareaTreeVo> all){
    List<TaareaTreeVo> childrens = all.stream().filter(m -> {
        return Objects.equals(m.getParentid(), root.getAreaid());
    }).map((m) -> {
                m.setChildren(getChildrens(m, all));
                return m;
            }
    ).collect(Collectors.toList());
    return childrens;
}

调用

//先直接获取所有的节点
List<TaareaTreeVo> taareaList = taareaReadMapper.queryAllTree(areacode);
//将节点先进行过滤获取到根节点,然后根据根节点,通过递归查询子节点
List<TaareaTreeVo> taareaTree = taareaList.stream().filter((m) -> "0".equals(m.getParentid())).map((m) -> {
    m.setChildren(getChildrens(m, taareaList));
    return m;
}).collect(Collectors.toList());
System.out.println("===输出结果====");
System.out.println(JSON.toJSON(taareaTree));

代码解释:

1、获取到行政区划树的所有节点,将装节点的list转成流

 taareaList.stream()

stream():对list 进行流化化操作,这是Collection类中的方法(List继承Conllection),能根据list返回一个对集合中的元素进行顺序排列有序的流

*当spliterator()方法不能返回spliterator时,应重写此方法

img

2、对流进行过滤

taareaList.stream().filter((m) -> "0".equals(m.getParentid()))

filter(boolean)对流进行过滤操作, 遍历每个元素,进行比较,把所有条件符合的返回形成新的stream。( "0".equals(m.getParentid())便是条件,得到返回true的元素)

中间操作(intermediate operation),得到的是一个stream对象

img

3、获取元素的映射,进行递归操作,查询下级子节点

taareaList.stream().filter((m) -> "0".equals(m.getParentid())).map((m) -> { m.setChildren(getChildrens(m, taareaList)); return m; }) 

map(function)方法用于映射每个元素到对应的结果,map((m) -> { m.setChildren(getChildrens(m, taareaList)); return m; })中 ,(m)既获取到的流中的每个元素(Lambda 表达式),然后对每个元素进行操作。map()会把执行function后的返回值放入一个新的stream中,然后返回新的流

中间操作(intermediate operation)

img

4、对处理后的流进行类型转换,转换成最后我们需要的List类型

终端操作

taareaList.stream().filter((m) -> "0".equals(m.getParentid())).map((m) -> {
                m.setChildren(getChildrens(m, taareaList));
                return m;
            }).collect(Collectors.toList());

collect() 是一个终止方法,将流进行保存,Stream不调用终止方法,中间的操作不会执行

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串。

//<R> – the type of the result
//<A> – the intermediate accumulation type of the Collector
<R, A> R collect(Collector<? super T, A, R> collector);

总结:

1、stream与集合的区别:

①无存储,流不是存储元素的数据结构,它只是某种数据源的一个视图,数据源可以是一个数据结构、数组、生成器函数或I/O通道;它通过计算操作的管道传递来自数据源的元素。

②流上的操作产生一个结果,但不修改其源。例如,过滤从集合中获得的Stream会生成一个没有过滤元素的新Stream,而不是从源集合中删除元素。

③惰式执行。许多流操作,如过滤、映射或重复删除,可以延迟实现,从而暴露出优化的机会。例如,“查找包含三个连续元音的第一个String”不需要检查所有的输入字符串。流操作分为中间(产生流)操作和终端(产生值)操作。中间操作总是惰性的(既操作并不会立即执行,只有当有终端操作时,才会执行中间操作)。

④可无上限。集合的大小是有限的,而流则不需要。像limit(n)或findFirst()这样的操作可以允许在有限的时间内完成无限流上的计算。

⑤可消费性。stream的元素在其生命周期中只会被访问一次。一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

⑥内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。stream使用内部迭代的方式, 通过访问者模式(Visitor)实现。

2、生成流的方式:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流。

  • 当并行执行时,多个中间结果可能被实例化、填充和合并,以保持可变数据结构的隔离。因此,即使在与非线程安全的数据结构(如ArrayList)并行执行时,也不需要额外的同步来进行并行缩减。

    获取流的方式(中间操作)

    stream 可以分为串行流(sequential streams)、并行流(parallel streams)和无序流(unordered streams)

    ①串行流:对于串行流,不论流中元素的出现顺序是否已经定义好都不影响性能,只影响结果。如果流是有序的,对完全相同的流执行重复的操作会产生完全相同的结果。

    list.stream().sequential()//是基础流串行化,获取串行流

    ②并行流:对元素可并行处理的流可以转换成并行流进行处理,使中间操作处理更高效。

    并行流上的操作必须满足三个特性:

    a、无状态:对流中的每个元素单独处理,与流中其他元素无关(例:filter就是无状态的中间操作,只与当时操作的元素有关,而sorted就是有状态的,它同时和 比较和被比较元素有关)

    b、不干预 :操作不会改变流

    c、关联性:及在方法中先处理哪个元素不影响逻辑和结果

    -list.stream().parallel()//解除顺序的限制,获取并行流
    list.parallelStream()//直接获取并行流

    ③无序流:流是否有序是指其元素出现顺序。有序流既 要么数据源有序,如:List、Arrays等,要么中间操作使流有序,如sorted()。

    如果一个数据源无序,且中间操作不进行排序,则是一个无序流。(无序流能提升某些中间操作的并发性能)

     list.stream().unordered()

3、常用中间操作:

<!--释义:类名::方法名 可以直接调用类下的该方法,详情见底-->

①filter: filter 方法用于通过设置的条件过滤出元素。Predicate断言,返回true/false,拿到为true的值的流

Stream<T> filter(Predicate<? super T> predicate);

例:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

②limit:limit 方法用于获取指定数量的流。

ints:返回一个有效的无限制的伪随机int值流。(无穷大的int类型的数字流必须limit截断使用)

例:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

③sorted:sorted 使用自然顺序对流的元素进行排序。元素类必须实现Comparable接口,默认升序

例:

list.stream().sorted().forEach(System.out::println);

若是想使用降序排序则使用Comparator提供reverseOrder()方法

例:

list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);

若想自定义的排序,可以传入Comparator作为参数

Stream<T> sorted(Comparator<? super T> comparator);

例:

taareaList.stream().sorted(Comparator.comparing(TaareaTreeVo::getAreaid)).forEach(System.out::println);

例:降序(将比较器翻转,相当于逆序)

taareaList.stream().sorted(Comparator.comparing(TaareaTreeVo::getAreaid).reversed()).forEach(System.out::println);

④distinct:distinct()去重,返回由该流的不同元素组成的流。distinct()使用hashCode()和equals()方法来获取不同的元素,因此必须重写equels()和hashCode()

处理有序流,那么对于重复元素,将保留以遭遇顺序首先出现的元素。

list.stream().dictinct().count();//计算不重复元素个数

有序流的并行流的情况下,保持distinct()的稳定性需要大量的缓冲开销,所以若不强制要求考虑数据顺序,可以使用BaseStream.unordered()来使流变为无序,从而节省空间。

例:

 list.stream().unordered().distinct().findFirst();//每次获取第一个元素,可能等,可能不等,是随机取的

⑤map:map(function)对流中的每个元素进行映射,进行function处理,将返回值放入新的流中(例如最上方的递归查询子元素)

<!--双冒号(::):方法引用(method reference)-->

静态方法引用(static method)语法:classname::methodname 例如:Person::getAge 对象的实例方法引用语法:instancename::methodname 例如:System.out::println 对象的超类方法引用语法: super::methodname 类构造器引用语法: classname::new 例如:ArrayList::new 数组构造器引用语法: typename[]::new 例如: String[]:new

<!--END-->

4、终端操作(缩减操作)

将流的元素合成一个总的结果。

①collect():

方法一:参数使用Collector。

Params:collector – the Collector describing the reduction  //Collector
Type parameters:
<R> – the type of the result  //返回值类型
<A> – the intermediate accumulation type of the Collector //集合元素类型
Returns:
the result of the reduction
<R, A> R collect(Collector<? super T, A, R> collector);

Collectors中提供了一系列Collector(规约操作执行器),可直接调用来求值或转换成所需数据类型。

例:

//最大值
Collectors.maxBy();
//最小值
Collectors.minBy();
//总和
Collectors.summingInt();/Collectors.summingDouble();/Collectors.summingLong();
//平均值
Collectors.averagingInt();/Collectors.averagingDouble();/Collectors.averagingLong();
//总个数
Collectors.counting();
//转换成List
Collectors.toList();
//拼接
Collectors.joining();//joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix); ---三个参数分别为分隔符、前缀、后缀
//转换成map
Collectors.toMap(key, value, (v1, v2) -> v1);//参数分别为:主键、值、key冲突时存哪个value
//分组
Collectors.groupingBy();//可分多个组
//分区
Collectors.partitioningBy();//分区只能分两个 true/false
分组:
//接收两个参数: 1.Function 参数  2.Collector多级分组

groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
​
例:
 studentStream.collect(Collectors.groupingBy(s -> {
            if (s.getScore() >= 60) {
                return "及格";
            } else {
                return "不及格";
            }
        }));
​

②reduce:缩减操作,指定的计算模型将Stream中的值计算得到一个最终结果

// 第一种
Optional<T> reduce(BinaryOperator<T> accumulator);
// 第二种
T reduct(T identity, BinaryOperator<T> accumulator);
// 第三种
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

--identity 元素,既是缩减的初始操作元素(seed value),也是在流中没有元素时缩减的默认返回值(reduce 的返回类型与identity 类型相同)。

--accumulator函数(累加器),输入一个局部结果和下一个元素,输出一个新的局部结果(会作为下次累加器计算的第一个参数)

--combiner函数,将两个局部结果合并,产生一个新的局部结果(它必须与accumulator函数兼容)

例:

   ArrayList<Integer> newList = new ArrayList<>();
        ArrayList<Integer> accResult_ = Stream.of(2, 3, 4)
                .reduce(newList,
                        (acc, item) -> {
                            acc.add(item);
                            System.out.println("item: " + item);
                            System.out.println("acc+ : " + acc);
                            System.out.println("BiFunction");
                            return acc;
                        }, (acc, item) -> null);
​
//最后会得到一个ArrayList类型的数据 

reduce 详细可以看看这个大佬的:Java 1.8 新特性——Stream 流中 Reduce 操作_CSDN-Lemon的博客-CSDN博客_stream的reduce

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值