[Java]-Java8中stream用法详解


Stream是Java8中处理集合的抽象概念,可以执行复杂的查找、过滤和映射等操作。在

操作API

Stream API 提供了一种高效且易于使用的处理数据的方式:

  • 不是数据结构,不会保存数据;
  • 不修改数据源,操作结果会保存到结果对象中;
  • 惰性求值:中间处理过程只是操作记录,不会立即执行;而是等到终止操作时才会进行实际计算;

接口中函数参数可以传递Lambd表达式或者签名相同的类函数(此时需要ClsName::funName样式),以groupingBy为例:

// 在流元素是字符串时,以下两者是等价的
groupingBy(String::length);
groupingBy(z->{return z.length;});

分类

操作接口可分为:

  • 无状态类:处理操作不受之前元素影响;
  • 有状态类:操作需拿到所有元素后才会继续下一步处理;
  • 非短路操作:必须处理所有元素才会得到最终结果;
  • 短路操作:找到符合条件的元素即得出最终结果;
Stream API
中间操作无状态
  • unordered
  • filter:过滤元素,只有满足条件的才放入结果集
  • map
  • mapToXX(Int/Long/Double)
  • faltMap
  • peek
有状态
  • distinct:根据元素的hashCode和equals去除重复元素
  • sorted:默认使用Comparable接口,也可传递Comparator接口来自定义排序
  • limit:获取n个元素
  • skip:跳过n个元素,与limit结合可实现分页
结束操作非短路操作
  • forEach
  • forEachOrdered
  • toArray
  • reduce
  • collect:收集结果,常与Collectors配合使用
  • max/min/count
短路操作
  • anyMatch
  • allMatch
  • nonMatch
  • findFirst
  • findAny

接口说明

映射

  • map:接收源中的元素,返回做为结果集中的元素;

  • flatMap:与map类似,但当元素为stream时,把多个stream合成一个做为结果返回。

List<String> lstAll = Arrays.asList("hello", "world");
// 输出两个数字:[h, e, l, l, o]与[w, o, r, l, d]
lstAll.stream()
        .map(s->{return s.split("");})
        .forEach(s->{
            System.out.println(Arrays.toString(s));
        });

// 输出每个字符:h\n e\n。。。
lstAll.stream()
        .flatMap(s->{return Stream.of(s.split(""));})
        .forEach(System.out::println);

创建stream

Java8中的大部分集合都有stream()接口,可直接获取stream。

Collection.stream

Java中集合都继承或实现了Collection.stream()接口:

Arrays.asList(3, 5, 7, 16, 18)
        .stream()
        .filter(z->{return z%2==0;})
        .forEach(System.out::println); // 输出偶数

Arrays.stream

通过Arrays.stream()可生成数组的流:

Integer[] array = new Integer[]{3, 4, 8, 16, 19};
long count = Arrays.stream(array)
        .filter(i -> i > 10)
        .count();

Stream.of

通过Stream.of()可把指定的元素(单个、或多个)变成流:

List<Integer> lstAll = Stream.of(1, 3, 5, 7)
        .map(z -> {
            return z * 2;
        })
        .collect(Collectors.toList());

无限流

对于无限流,需要通过limit来限制最终流的长度。

Stream.generate

通过Stream.generate()可生成无限长度的流(根据指定的规则):

Stream.generate(new Random()::nextInt)
        .limit(5).forEach(System.out::println);
        // 输出5个随机数

Stream.iterate

通过Stream.iterate()也可生成无限长度的流,其接受一个参数与一个生成函数:

Set<Integer> setAll =  Stream.iterate(1, n->{return n*2;})
        .limit(5)
        .collect(Collectors.toSet());
System.out.println(setAll);

Collectors

根据不同的策略将元素收集归纳起来,比如最简单常用的是将元素装入MapSetList 等可变容器中。

归并到数组

List可通过toArray直接转,stream中也有toArray。

List<Integer> lstAll = Lists.newArrayList(1,2,3,4,5,6);

Integer[] aryInt = lstAll.toArray(new Integer[0]);
String[] aryStr = lstAll.stream()
        .map(Objects::toString)
        .toArray(String[]::new);

归并到集合

toXXX

  • toCollection:将流中的元素全部放置到一个集合(具体集合类型由参数决定,如Collectors.toCollection(LinkedList::new));
  • toList:转换为List返回;
  • toSet:转换为Set返回;
  • toMap:转换为Map返回;
List转Map

Collectors.toMap可方便地把其他集合转为Map;

List<String> lstTest = Arrays.asList("a123", "a456", "b789", "b890");
lstTest.stream()
        .collect(Collectors.toMap(
                e -> e.substring(0, 1), // Key
                e -> e,         // value
                (oldV, curV) -> curV,    // key重复时处理方式,替换掉
                HashMap::new)   // 可选项,使用map类型(如ConcurrentHashMap::new)
        )
        .forEach((k, v) -> System.out.println(k + "=" + v));
Map转List

Map中keySet、values、entrySet,分别是键、值、键值对的集合。

Map<Integer, CountData> mapData = new ConcurrentHashMap<>();
// ...
List<CountData> lstValue = mapData.values().stream()
        .collect(Collectors.toList());

mapping

相当于先map再转换为集合:map(XX).Colcollect(Collectors.toXX())

lstTest.stream().collect(
        Collectors.mapping(
                Integer::valueOf,	// map方式
                Collectors.toList()));

分组

groupingBy

生成一个拥有分组功能的Collector:默认是生成HashMap,且同一组中元素为ArrayList。

List<String> lstTest = Arrays.asList("a123", "a4561", "b789", "b8901");
lstTest.stream().collect(
        Collectors.groupingBy(
                String::length,     // 分组方式
                HashMap::new,       // 结果集Map类型
                Collectors.toSet())) // Map中Value(同一组中元素)集合类型
        .forEach((k, v) -> System.out.println(k + "=" + v));

partitioningBy

将流中的元素按照给定的校验规则的结果分为两个部分:满足条件与不满足条件的;Map的键值为true与false。

List<String> lstTest = Arrays.asList("a123", "a4561", "b789", "b8901");
lstTest.stream().collect(
        Collectors.partitioningBy(
                e -> e.length() > 4,    // 分类方式
                Collectors.toSet()))    // Map中Value(同一组中元素)集合类型,默认List
        .forEach((k, v) -> System.out.println(k + "=" + v));

计算与统计

  • counting:计数
  • minBy/MaxBy:获取最小/最大值的Optional结果;
  • summingInt/summingLong/summingDouble:求和;
  • averagingInt/averagingLong/averagingDouble:求平均值;

默认情况下,Stream中的Lambda表达式不允许修改外部的变量;为此可通过CAS变量做计数,或者通过数组变量方式处理:

List<Integer> lstAll = Lists.newArrayList(1,2,3,4,5,6);

AtomicInteger count = new AtomicInteger(0);
Boolean[] hasLarge = new Boolean[1]; // 也可以使他AtomicBoolean
hasLarge[0] = false;
lstAll.forEach(z->{
    if(z>3){
        hasLarge[0] = true;
        count.incrementAndGet(); // 普通Integer是无法修改的
    }
});

reduce

reducing是一个收集器(操作),可以用在多层流、下游数据分组或分区等场合。count(),max(),min(),sum()等是预定义好的收集器。

Comparator<Person> funHeight = Comparator.comparingInt(Person::getHeight);
Map<City, Optional<Person>> tallestByCity = personList.stream().
                collect(groupingBy(
					Person::getCity, 
					reducing(BinaryOperator.maxBy(funHeight))));

获取每个城市中最高者

collectingAndThen

在归并结果后,再对结果做进一步处理。

lstTest.stream().collect(
    Collectors.collectingAndThen(
            Collectors.groupingBy(String::length),
            z -> z.size()));

分组后,再获取分组数量(总共有几个分组)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值