Collections 如何实现Java8流fluent API和延迟计算

Collections 如何实现Java8流fluent API和延迟计算,collections,lambda,java-8,java-stream,higher-order-functions,Collections,Lambda,Java 8,Java Stream,Higher Order Functions,我想找出一个相当于Java 8的简单实现,它允许我探索延迟计算的查询算法的开发(例如map(),filter(),reduce(),等等)注意:我的目标不是实现比Stream更好的解决方案。另一方面,我唯一的目标是理解流的内部结构 然而,我发现的每个实现都是基于Iterable,如以下答案中提供的解决方案: 然而,我对这些解决方案并不满意,因为: 它们太冗长了 对于新的查询方法,它们不灵活。包含新的查询方法需要进行结构修改 尽管有查询参数,但它们没有利用Java8的新特性,例如:第一类

我想找出一个相当于Java 8的简单实现,它允许我探索延迟计算的查询算法的开发(例如
map()
filter()
reduce()
,等等)注意:我的目标不是实现比Stream更好的解决方案。另一方面,我唯一的目标是理解流的内部结构

然而,我发现的每个实现都是基于
Iterable
,如以下答案中提供的解决方案:

然而,我对这些解决方案并不满意,因为:

  • 它们太冗长了
  • 对于新的查询方法,它们不灵活。包含新的查询方法需要进行结构修改
  • 尽管有查询参数,但它们没有利用Java8的新特性,例如:第一类函数或默认方法
  • 它们都没有使用
    上使用的
    拆分器
    方法
  • 我知道,
    Spliterator
    被设计为允许分区和并行处理,但我认为它独特的迭代器方法(
    boolean-tryAdvance(Consumer)
    )可以被利用到比上面列出的新的替代方法。此外,作为:

    拆分器
    是一个更好的
    迭代器
    ,即使没有并行性。(它们通常更容易写,也更难出错。)

    那么,是否有可能开发一个更可读、更简单、简洁和灵活的查询API实现,该API是基于
    的相同原理(除了并行处理部分)进行惰性计算的

    如果是,你怎么做?我希望看到比上面列出的实现更简单的实现,如果可能的话,利用新的Java8特性

    要求

    • 不要重用Java8API中的现有方法
    • 并行处理功能超出了此问题的范围
    • 如果可能,最好不要使用
      Iterable
      方法
    我的问题的原因是什么?我认为学习Stream之类的查询API的最佳方法是尝试自己实现这些方法。我在学习.net Linq时已经成功地完成了这项工作。当然,我没有实现比Linq更好的实现,但这帮助我理解了内部部分。所以,我正试图遵循同样的方法来学习流

    这并不罕见。对于其他技术,有许多研讨会遵循这种方法,例如,大多数练习要求实现现有方法,例如:
    map()
    filter()
    reduce()
    call()
    bind()
    ,等等


    选择答案:目前我认为这是我的选择,而不是因为后者不允许实现
    findAny()
    findFirst()
    而不通过
    dataSrc的
    forEach()
    完全遍历整个元素。但是,我认为,由于基于
    forEach()
    的方法,通过使用函数式编程和利用Java 8默认方法,我们可以实现一个简短而干净的查询API延迟计算解决方案,从而减少迭代代码的开销,该迭代代码用于调解对数据结构内部的访问,如

    所述。例如,在下面的
    Queryable
    类型中检查如何轻松实现
    map()
    forEach()
    方法,然后您可以这样使用它:

    List<String> data = Arrays.asList("functional", "super", "formula");
    Queryable.of(data) // <=> data.stream().
         .map(String::length)
         .forEach(System.out::println);
    

    通过使用函数式编程并利用Java8默认方法,我们可以实现一个简短而干净的查询API延迟计算解决方案。例如,在下面的
    Queryable
    类型中检查如何轻松实现
    map()
    forEach()
    方法,然后您可以这样使用它:

    List<String> data = Arrays.asList("functional", "super", "formula");
    Queryable.of(data) // <=> data.stream().
         .map(String::length)
         .forEach(System.out::println);
    

    实现无状态操作子集非常容易,无需短路支持。您只需要注意始终坚持内部迭代。基本构造块是forEach操作,它可以为每个输入元素执行给定的操作。
    forEach
    方法的主体是唯一在不同阶段发生变化的东西。因此,我们可以使用abstract
    forEach
    方法创建抽象类,也可以接受实际上是
    forEach
    主体的函数。我将坚持第二种方法:

    public final class MyStream<T> {
        private final Consumer<Consumer<T>> action;
    
        public MyStream(Consumer<Consumer<T>> action) {
            this.action = action;
        }
    
        public void forEach(Consumer<T> cons) {
            action.accept(cons);
        }
    }
    
    现在,让我们使用
    forEach
    创建一些终端操作:

    public T reduce(T identity, BinaryOperator<T> op) {
        class Box {
            T val = identity;
        }
        Box b = new Box();
        forEach(e -> b.val = op.apply(b.val, e));
        return b.val;
    }
    
    public Optional<T> reduce(BinaryOperator<T> op) {
        class Box {
            boolean isPresent;
            T val;
        }
        Box b = new Box();
        forEach(e -> {
            if(b.isPresent) b.val = op.apply(b.val, e);
            else {
                b.val = e;
                b.isPresent = true;
            }
        });
        return b.isPresent ? Optional.empty() : Optional.of(b.val);
    }
    
    public long count() {
        return map(e -> 1L).reduce(0L, Long::sum);
    }
    
    public Optional<T> maxBy(Comparator<T> cmp) {
        return reduce(BinaryOperator.maxBy(cmp));
    }
    
    public Optional<T> minBy(Comparator<T> cmp) {
        return reduce(BinaryOperator.minBy(cmp));
    }
    
    等等。这种实现非常简单,但非常接近实际的流API。当然,当您添加短路、并行流、原始专门化和更多有状态操作时,事情会复杂得多

    请注意,与Stream API不同,此
    MyStream
    可以多次重复使用:

    MyStream<Integer> range = range(0, 10);
    range.forEach(System.out::println);
    range.forEach(System.out::println); // works perfectly
    
    MyStream范围=范围(0,10);
    range.forEach(System.out::println);
    range.forEach(System.out::println);//完美地工作
    
    实现无状态操作子集非常容易,无需短路支持。您只需要注意始终坚持内部迭代。基本构造块是forEach操作,它可以为每个输入元素执行给定的操作。
    forEach
    方法的主体是唯一在不同阶段发生变化的东西。因此,我们可以使用abstract
    forEach
    方法创建抽象类,也可以接受实际上是
    forEach
    主体的函数。我将坚持第二种方法:

    public final class MyStream<T> {
        private final Consumer<Consumer<T>> action;
    
        public MyStream(Consumer<Consumer<T>> action) {
            this.action = action;
        }
    
        public void forEach(Consumer<T> cons) {
            action.accept(cons);
        }
    }
    
    现在,让我们使用
    forEach
    创建一些终端操作:

    public T reduce(T identity, BinaryOperator<T> op) {
        class Box {
            T val = identity;
        }
        Box b = new Box();
        forEach(e -> b.val = op.apply(b.val, e));
        return b.val;
    }
    
    public Optional<T> reduce(BinaryOperator<T> op) {
        class Box {
            boolean isPresent;
            T val;
        }
        Box b = new Box();
        forEach(e -> {
            if(b.isPresent) b.val = op.apply(b.val, e);
            else {
                b.val = e;
                b.isPresent = true;
            }
        });
        return b.isPresent ? Optional.empty() : Optional.of(b.val);
    }
    
    public long count() {
        return map(e -> 1L).reduce(0L, Long::sum);
    }
    
    public Optional<T> maxBy(Comparator<T> cmp) {
        return reduce(BinaryOperator.maxBy(cmp));
    }
    
    public Optional<T> minBy(Comparator<T> cmp) {
        return reduce(BinaryOperator.minBy(cmp));
    }
    
    等等。苏
    MyStream<Integer> range = range(0, 10);
    range.forEach(System.out::println);
    range.forEach(System.out::println); // works perfectly