当前位置: 首页 > 工具软件 > Vavr > 使用案例 >

io.vavr.collection.Stream上手指南

侯向文
2023-12-01

前天在写Stream的排序代码时在想可不可以在排序时给结果一个排序编号呢?例如: 选取得分排名的前N名用户, 得分高的第一位,哪么它的编号自然是1,依次递增. 实现类似Top榜的结果集. 真是遗憾jdk原生不支持这样的功能. 一顿搜索后发现真有不少人有这样的需求: Is there a concise way to iterate over a stream with indices in Java 8?

1)开始吧!

        io.vavr.collection.Stream.of("Sam", "Pamela", "Dave", "Pascal", "Erik").sorted().zipWithIndex().forEach(tuple ->{
            System.out.println("index="+tuple._2+",value="+tuple._1);
        });

结果像你期望的哪样.Dave排在最前面,不过它的编号是从0开始的, 这不是问题。刚才我看了jdk 11的文档也是可以实现的

    @Test
    public void testRI(){
        AtomicInteger index = new AtomicInteger(0);
        Stream.of("Sam", "Pamela", "Dave", "Pascal", "Erik").sorted().forEachOrdered(str->{
            int rk = index.getAndIncrement();
            System.out.println("index="+rk+",value="+str);
        });
    }

让我们开始看一看vavr的Stream是何东东吧. 官方的文档地址: io.vavr.collection,Stream类的描述是: An immutable Stream is lazy sequence of elements which may be infinitely long. 中文大致意思是一个不可变的 惰性序列. 可以无限长。jdk 的Stream给我的感觉更像是一种处理集合的技术, jdk8以前集合的遍历是通过外部循环.而jdk8+用stream则是内部遍历,在代码的外在表现上看不到for和while这样的循环语句。所以两者在设计思想上是不同的.

    @Test 
    public void testMem(){
        Stream<Integer> cacheStream = Stream.rangeClosed(1, 10).prepend(0).append(11);
        System.out.println(cacheStream.last()+"< last Integer");
        System.out.println(cacheStream.head()+"> first Integer");
    }

第一行打印结果是11,第二行打印结果是0. 从这个测试可以看出vavr的Stream设计成了一个集合类型,jdk的Stream只能消费一次,而上面的代码更符合jdk Collection的思想.

注意: 代码中的Stream是io.vavr.collection.Stream

2)jdk Stream的多次消费

Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true);    // ok
stream.noneMatch(s -> true);   // exception

也是有方法实现多次消费的, A: 变量类型为List或其它集合类型, 每次消费时再转成Stream. B: 使用Supplier来封装,每次消费时先调用get方法

Supplier<Stream<String>> streamSupplier = () -> Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok

读过java 实战第二版(modern java in action)的小伙伴肯定可以看到StreamForker的思路. Forked streams, 代码自已去撸吧。这里我贴出测试用例:

public class StreamTest {
    private final Stream<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        ).stream();
    
    @Test @Ignore
    public void testRs() {
        ForkedStreamResults rs = new StreamForker<Dish>(menu)
                        .fork("shortMenu", s->s.map(Dish::getName).collect(Collectors.joining(",")))
                        .fork("totalCalories", s->s.mapToInt(Dish::getCalories).sum())
                        .fork("mostCaloricDish", s->s.collect(Collectors.reducing((d1,d2)->d1.getCalories() > d2.getCalories()?d1:d2)).get())
                        .getResults();
        System.out.println("shortMenu: "+rs.get("shortMenu"));
        System.out.println("totalCalories: "+rs.get("totalCalories"));
        System.out.println("mostCaloricDish: "+rs.get("mostCaloricDish").toString());
    }
}

下面是用vavr Stream实现的测试用例:

public class VavrTest {
    private final java.util.stream.Stream<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        ).stream();
    @Test
    public void testVavr(){
        Tuple3<String, Long, String> result = Stream.ofAll(menu).unzip3((Dish dish)->{
            return new Tuple3<String, Integer, Dish>(dish.getName(), dish.getCalories(), dish);
        }).map(
            mapper1->mapper1.collect(Collectors.joining(",")), 
            mapping2->mapping2.sum().longValue(), 
            mapping3->mapping3.collect(Collectors.reducing((d1,d2)->d1.getCalories() > d2.getCalories()?d1:d2)).get().toString());
        
        System.out.println("shortMenu: "+result._1);
        System.out.println("totalCalories: "+result._2);
        System.out.println("mostCaloricDish: "+result._3);
    }
}

两个测试用例的打印结果是一样的. 从代码风格上看vavr Stream更贴合函数编程的思维.

3)vavr Stream的示例

3.1 随机

    @Test 
    public void testSuffer(){
        //随机排列
        String a = Stream.ofAll(menu).shuffle().map(Dish::getName).collect(Collectors.joining(","));
        System.out.println("rnd value: "+a);
    }

shuffle还有一个重载的方法可以传入一个java.util.Random

3.2 压缩

    @Test 
    public void testZip(){
        List<Tuple2<Integer, String>> tuple2List = Stream.ofAll(1, 2, 3).zip(List.of("a", "b", "c")).toList();
        tuple2List.forEach(tuple->{
            System.out.println("int: "+tuple._1+", str: "+tuple._2);
        });
    }

注意: 代码中的List是io.vavr.collection.List

3.3 分区

    @Test
    public void testSection(){
        Tuple2<String, String> result = Stream.ofAll(menu).partition(Dish::isVegatarian).map((mapper1, mapper2)->{
            return new Tuple2<String,String>(mapper1.map(Dish::getName).collect(Collectors.joining(",")), mapper2.map(Dish::getName).collect(Collectors.joining(",")));
        });
        System.out.println("isVegatarian true: "+result._1);
        System.out.println("isVegatarian false: "+result._2);
    }

使用jdk Stream的Collectors.partitioningBy也可以分区

4)vavr的学习资源

4.1 官方的使用指南: Vavr User Guide

关于Stream的介绍只有几行文字. 其实只要搞懂的jdk的Function知识,其实上手不难. 我还是希望有一些示例

4.2 IBM Dev社区的文章: 使用 Vavr 进行函数式编程

关于Stream的示例倒是有几个.

4.3 baeldung.com: Introduction to Vavr

只找到这几个有异同的文章, 国内也是一些是翻译或转载的, 这里都不贴了留给你白渡吧

 类似资料: