MapTill的Java 8分叉流

MapTill的Java 8分叉流,java,java-8,java-stream,Java,Java 8,Java Stream,我最近开始玩java streams。 现在我想到了 Stream<T> mapUntil(Stream<T> in, Function<T,T> mapFunc, Predicate<Stream<T>> predicate) 这显然有很多问题 它不适用于无限(或非常大)的流。在我的特殊情况下,这是可以接受的,但以这样一种普遍的方法来宣称这一点感觉很糟糕 它不利于流的插入,因为所有的惰性都会丢失-由于collect(collect

我最近开始玩java streams。 现在我想到了

Stream<T> mapUntil(Stream<T> in, Function<T,T> mapFunc, Predicate<Stream<T>> predicate)
这显然有很多问题

  • 它不适用于无限(或非常大)的流。在我的特殊情况下,这是可以接受的,但以这样一种普遍的方法来宣称这一点感觉很糟糕
  • 它不利于流的插入,因为所有的惰性都会丢失-由于
    collect(collector.toList())
我试图修改代码以消除第二个问题,即重写
applyUntil

Stream<T> applyUntil(Stream<T> in, Function<Stream<T>,Stream<T>> func,
  Predicate<Stream<T>> predicate){
    List<T> collected = in.collect(Collectors.toList());
    return applyUntil(()->collected.stream(),func,predicate);
}
Stream<T> applyUntil(Supplier<Stream<T>> sup, Function<Stream<T>,Stream<T>> func,
  Predicate<Stream<T>> pred){
    if(predicate.test(sup.get())) return sup.get();
    return applyUntil(()->func.apply(sup.get()),func,predicate);
}
通过这些课程,我可以将我的方法重写为:

Stream<T> applyUntil(Stream<T> in, Function<Stream<T>,Stream<T>> func,
  Predicate<Stream<T>> predicate){
    Fork<T> fork = new Fork(new Convert<T>(in.iterator()));
    Stream<T> master = StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(fork.getMasterIter(),0),false);
    Stream<T> slave = StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(fork.getSlaveIter(),0),false);
    if(predicate.test(master)) return slave
    return applyUntil(func.apply(slave,func,predicate);
}
Stream applyUntil(Stream-in,Function func,
谓词(谓词){
Fork-Fork=newfork(newconvert(in.iterator());
Stream master=StreamSupport.Stream(
Spliterators.spliteratorUnknownSize(fork.getMasterIter(),0),false);
从流=StreamSupport.Stream(
Spliterators.spliteratorUnknownSize(fork.getslavier(),0),false);
if(谓词测试(主)返回从
返回applyUntil(函数apply(从属、函数、谓词);
}
它确实适用于无限流,它仍然是懒惰的,它重用计算值。在我看来,这就像一个适用于各种用途的一体化设备

编辑:当我试图解释最后一个代码块无法编译的原因时,我找到了一种方法让它编译。它仍然不是很好,失去了很多流魔法,不是线程保存等等。此外,MyStream应该有一个
close
方法,如果你不再对任何数据感兴趣,它会发出信号-所以
Fork
不会我不必为你保存它。所以我脑海中出现了另一个问题:你能用迭代器之类的东西创建一个“j.u.stream.stream”吗?它告诉迭代器它不再对任何数据感兴趣(因为短路)

所以我的问题是:没有外部库的JDK8是否有像我的叉子这样的东西,让更多的魔法保持活力? 如果是:哪门课/方法可以帮助我? 如果没有:为什么不呢?还有:你怎么能自己实现它,让尽可能多的魔法保持活力

感谢您的阅读,很抱歉写了这么长的文字:/
Alex

Java 9将具有and。将它们与and结合起来,您就可以做到

Stream.concat(
  sourceCollection.stream().takeWhile(predicate).map(mapper), 
  sourceCollection.stream().dropWhile(predicate.negate())
)
这不会为中间收集消耗额外内存,但会通过遍历前缀两次来消耗CPU时间,除非它在到达第二个流之前会短路


为了获得更有效的解决方案,您可以通过提取
stream.spliterator()来实现中间状态操作,例如条件映射函数
,将其包装到
Spliterator
-或
j.u.Spliterators.AbstractSpliterator
的自定义子类中(如果您懒于实现并行支持),然后使用
j.u.s.StreamSupport.stream(Spliterator,布尔值)
将其包装回流中。

首先,必须纠正您对性能问题的分析。在第二个变体中,调用
func.apply
的频率无关紧要,因为该函数不执行任何工作。它所做的只是在稍后处理的流上链接另一个中间操作,但是取决于该处理,它对性能的影响有多大

在这方面,您过于关注大流甚至无限流上的短路操作,这些操作恰好在您的特定设置中很早就完成了。基本问题是,您的每个过滤步骤可能会处理所有流元素,并且必须在下一个过滤步骤之前完成,并且存在一个过滤步骤的可预测数量不会改变

如果您总是使用谓词和流元素的组合来允许早期短路,这会使您的第三个解决方案大放异彩,但请注意,在这些情况下,您的第二个解决方案的问题不是嵌套函数应用程序,而是在此之前您仍然将整个初始流收集到
列表中跳过该步骤并调用接受供应商的方法首先,您不会遇到这些问题

然后,根据链接到流的实际中间操作的权重,将元素缓冲到
Deque
中与否是一个小的权衡。请注意,您可以使用流API执行第三种方法中的操作,而无需镜像它:

/** returns a {@code List} containing two {@code Stream}s */
public static <T> List<Stream<T>> fork(Stream<T> source) {
    Spliterator<T> srcSp=source.spliterator();
    ArrayDeque<T> deque=new ArrayDeque<>();
    Boolean[] ahead={ null };
    final class Branch extends Spliterators.AbstractSpliterator<T> {
        private final Boolean type;
        Branch(Boolean b) {
            super(srcSp.estimateSize(), srcSp.characteristics());
            type=b;
        }
        public boolean tryAdvance(Consumer<? super T> action) {
            if(deque.isEmpty() || ahead[0]==type) {
                if(!srcSp.tryAdvance(deque::push)) return false;
                ahead[0]=type;
                action.accept(deque.peek());
                return true;
            }
            action.accept(deque.removeLast());
            return true;
        }
    }
    return Arrays.asList(
        StreamSupport.stream(new Branch(true),  false),
        StreamSupport.stream(new Branch(false), false));
}
public static <T> Stream<T> applyUntil(
        Stream<T> in, Function<Stream<T>,Stream<T>> func, Predicate<Stream<T>> predicate) {
    List<Stream<T>> fork = fork(in);
    return predicate.test(fork.get(0))? fork.get(1):
        applyUntil(func.apply(fork.get(1)), func, predicate);
}
/**返回包含两个{@code Stream}的{@code List}*/
公共静态列表分叉(流源){
Spliterator srcSp=source.Spliterator();
ArrayDeque deque=新的ArrayDeque();
布尔[]超前={null};
最后一个类分支扩展了Spliterators.AbstractSpliterator{
私有最终布尔类型;
分支(布尔b){
super(srcSp.estimateSize(),srcSp.characteristics());
类型=b;
}

公共布尔TrayAdvices(消费者感谢很多有趣的问题。我在理解你的解决方案的过程中,你介意把代码更新到可编译状态吗?EX:<代码> Foo.Master < /Cord>应该是<代码> java .UTI.Struts。流<代码>,对于<代码>
@tkachuko当我试图解释为什么这是不可能的时候,我想出了一个编译版本,你现在可以看到这个。它仍然不是很好,并引出了另一个问题-但至少它现在可以工作了,谢谢:)imo
takeWhile
将采用
谓词,但我正在寻找一个“修改流直到”这需要一个
谓词
-所以我不明白在我的情况下如何使用
dropWhile
takeWhile
。我回答的第二句解释了如何使用它…哪一部分不清楚?好吧,看起来我当时不明白你问题的意图。你的意思是你想对每一部分应用映射函数重复测试,直到满足某个条件,因为你不能在整个测试中测试任何东西
interface MyStream<T>{
    T get();
    boolean hasNext();
}
class Convert<T> implements MyStream<T>{
     Iterator<T> inner;
     pulic Convert(Interator<T> iter){
          inner=iter;
     }
     public boolean hasNext(){
          return inner.hasNext();
     }
     public T get(){
          return inner.get();
     }
class AddFirst<T> implements MyStream<T>{
     T item;
     MyStream<T> inner;
     boolean used;
     public AddFirst(T t, MyStream<T> prev){
         item=t;
         inner=prev;
         used=false;
     }
     public T get(){
          if(used) return inner.get();
          used=true;
          return item;
     }
     public boolean hasNext(){
         return !used || inner.hasNext();
     }

}
class Filter<T> implements MyStream<T>{
     Predicate<T> filter;
     MyStream<T> inner
     public Filter(Predicate<T> test, MyStream<T> prev){
         filter=test;
         inner=prev;
     }
     public T get(){
          while(true){
              T curr = inner.get(); //if !inner.hasNext, this throws NoSuchElementException
              if(filter.test(curr)) return curr;
          }
     }
     public boolean hasNext(){
         try{
             T item = get();
             inner = new AddFirst(item,inner);
             return true;
         }
         catch(NoSuchElementException e){
             return false;
         }
     }
}
class Map<K,T> implements MyStream<T>{
    MyStream<K> inner;
    Function<K,T> func;
    public Map(Function<K,T> func,MyStream<K> prev){
        this.func=func;
        inner = prev;
    }
    public T get(){
        return func.apply(inner.get());
    }
    public boolean hasNext(){
        return inner.hasNext();
    }
}
class Forall<T> implements Predicate<MyStream<T>>{
    Predicate<T> pred;
    public Forall(Predicate<T> func){
        pred=func;
    }
    public boolean test(MyStream<T> ms){
        while(ms.hasNext()){
            if(!pred.test(ms.get()) return false;
        }
        return true;
    }
}
class Exists<T> implements Predicate<MyStream<T>>{
    Predicate<T> pred;
    public Forall(Predicate<T> func){
        pred=func;
    }
    public boolean test(MyStream<T> ms){
        while(ms.hasNext()){
            if(pred.test(ms.get()) return true;
        }
        return false;
    }
}
class Fork<T>{
    Deque<T> advance;
    MyStream<T> inner;
    boolean ahead;
    MyStream<T> master;
    MyStream<T> slave;
    public Fork(MyStrem<T> prev){
         inner=prev;
         advance= new LinkedList<T>();
         ahead=false;
         master = new ForkStream(true);
         slave = new ForkStream(false);
    }
    public MyStream<T> getMaster(){
        return master;
    }
    public Iterator<T> getMasterIter(){
        return master;
    }
    public MyStream<T> getSlave(){
        return slave;
    }
    public Iterator<T> getSlaveIter(){
        return slave;
    }
    class ForkStream implements MyStream<T>, Iterator<T>{
         boolean role;
         public ForkStream(boolean in){
             role=in;
         }
         public T get(){
              if(role==ahead||advance.size()==0){
                 ahead=role;
                 T item = inner.get();
                 advance.addLast(item);
                 return item;
              }
              else{
                  return advance.removeFirst();
              }
         }
         public boolean hasNext(){
               return (role!=ahead&&advance.size()!=0) || inner.hasNext();
         }
         public T next(){
               return get();
         }
    }
}
Stream<T> applyUntil(Stream<T> in, Function<Stream<T>,Stream<T>> func,
  Predicate<Stream<T>> predicate){
    Fork<T> fork = new Fork(new Convert<T>(in.iterator()));
    Stream<T> master = StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(fork.getMasterIter(),0),false);
    Stream<T> slave = StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(fork.getSlaveIter(),0),false);
    if(predicate.test(master)) return slave
    return applyUntil(func.apply(slave,func,predicate);
}
Stream.concat(
  sourceCollection.stream().takeWhile(predicate).map(mapper), 
  sourceCollection.stream().dropWhile(predicate.negate())
)
/** returns a {@code List} containing two {@code Stream}s */
public static <T> List<Stream<T>> fork(Stream<T> source) {
    Spliterator<T> srcSp=source.spliterator();
    ArrayDeque<T> deque=new ArrayDeque<>();
    Boolean[] ahead={ null };
    final class Branch extends Spliterators.AbstractSpliterator<T> {
        private final Boolean type;
        Branch(Boolean b) {
            super(srcSp.estimateSize(), srcSp.characteristics());
            type=b;
        }
        public boolean tryAdvance(Consumer<? super T> action) {
            if(deque.isEmpty() || ahead[0]==type) {
                if(!srcSp.tryAdvance(deque::push)) return false;
                ahead[0]=type;
                action.accept(deque.peek());
                return true;
            }
            action.accept(deque.removeLast());
            return true;
        }
    }
    return Arrays.asList(
        StreamSupport.stream(new Branch(true),  false),
        StreamSupport.stream(new Branch(false), false));
}
public static <T> Stream<T> applyUntil(
        Stream<T> in, Function<Stream<T>,Stream<T>> func, Predicate<Stream<T>> predicate) {
    List<Stream<T>> fork = fork(in);
    return predicate.test(fork.get(0))? fork.get(1):
        applyUntil(func.apply(fork.get(1)), func, predicate);
}