Java 如何根据谓词筛选Iterable?

Java 如何根据谓词筛选Iterable?,java,java-8,predicate,iterable,java-stream,Java,Java 8,Predicate,Iterable,Java Stream,我想使用一个Iterable和一个谓词来执行一个字符串列表过滤函数,以选择要保留的字符串,其他字符串必须从列表中删除,但我并不低估如何删除 static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) { for (T s: it) { if (pred.test(s)==false) { // what to do here?

我想使用一个
Iterable
和一个谓词来执行一个字符串列表过滤函数,以选择要保留的字符串,其他字符串必须从列表中删除,但我并不低估如何删除

static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    for (T s: it) {
        if (pred.test(s)==false) {
            // what to do here?
        }
    }
    return ...;
}
我想

{"a","b"}

由于任何
集合
都是
Iterable
,只需将符合条件的项目添加到新集合中,然后返回:

static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    Collection<T> collection = new ArrayList<>();
    for (T s: it) {
        if (!pred.test(s)) {
            collection.add(s);
        }
    }
    return collection;
}

Iterable表示根据请求提供迭代器的能力。因此,要用过滤逻辑装饰现有的iterable,必须实现装饰
迭代器

static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    return () -> new Iterator<T>() {
        Iterator<T> sourceIterator = it.iterator();
        T current;
        boolean hasCurrent;

        @Override
        public boolean hasNext() {
            while(!hasCurrent) {
                if(!sourceIterator.hasNext()) {
                    return false;
                }
                T next = sourceIterator.next();
                if(pred.test(next)) {
                    current = next;
                    hasCurrent = true;
                }
            }
            return true;
        }

        @Override
        public T next() {
            if(!hasNext()) throw new NoSuchElementException();
            T next = current;
            current = null;
            hasCurrent = false;
            return next;
        }
    };
}

首先将您的
Iterable
包装到
流中

  • 纯Java:

    StreamSupport.stream(it.spliterator(), false)
    
然后根据您的
谓词对其进行过滤:

最后返回
Iterable

  • 作为
    lambda

    return () -> stream.iterator();
    
  • 作为
    方法参考

    return stream::iterator;
    

完整示例:

static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    return StreamSupport.stream(it.spliterator(), false).filter(pred.negate())::iterator;
}
static Iterable select(Iterable it,Predicate pred){
返回StreamSupport.stream(it.spliterator(),false).filter(pred.negate())::迭代器;
}
或:

static Iterable select(Iterable it,Predicate pred){
Stream=Stream(it.spliterator(),false);
谓词negatedPred=pred.negate();
Stream filteredStream=Stream.filter(negatedPred);
返回filteredStream::迭代器;
}

我在评论中提到的霍尔格的替代方案如下所示:

static <T> Iterable<T> select(Iterable<T> toIterate, Predicate<T> pred) {
    return () -> new Iterator<T>() {
        Iterator<T> delegate = toIterate.iterator();
        T next = findNextValid();
        public boolean hasNext() {
            return next != null;
        }
        public T next() {
            if (next == null) throw new NoSuchElementException();
            T result = next;
            next = findNextValid();
            return result;
        }
        private T findNextValid() {
            T result = null;
            while (result == null && delegate.hasNext()) {
                T candidate = delegate.next();
                if (pred.test(candidate)) {
                    result = candidate;
                }
            }
            return result;
        }
    };
}
static Iterable select(Iterable到iterate,谓词pred){
return()->new Iterator(){
迭代器委托=toIterate.Iterator();
T next=findNextValid();
公共布尔hasNext(){
返回下一步!=null;
}
公共交通工具{
如果(next==null)抛出新的NoSuchElementException();
T结果=下一个;
next=findNextValid();
返回结果;
}
私有T findNextValid(){
T结果=null;
while(result==null&&delegate.hasNext()){
T candidate=delegate.next();
if(预备测试(候选)){
结果=候选人;
}
}
返回结果;
}
};
}

不同之处在于,
hasCurrent
不需要额外的标记,它在实际请求下一个元素之前推进
迭代器。你可能会认为后者是不受欢迎的。

PETEVE:<代码> PRED。测试(S)= = false < /COD>是坏的风格。使用
!预先测试
Ty提示!顺便说一句,您想要的方法已经存在于Guava中:。检查它们的实现。这是一个解决方案,但不是一个有效的解决方案。最好实现一个能够跳过项的自定义迭代器——这就是Guava的实现所做的collection@GFAA所有
Collection
s都是
Iterable
s。他分配给
Collection
,这样他就可以向其中添加项目。这样,我就可以检索列表或数组列表,而不是Collection和iterable了?@Michael是的,有很多方法。我不希望仅仅为了这个用例而导入番石榴。然而,用一个已经存在的实现来“启发”是一个很好的实践:)guava在内部使用一个
AbstractIterator
解决了这个复杂的模式,该方法只有一个
computeNext
方法IIRC;与
tryAdvance
的想法几乎相同。这使得它们可以轻松实现
Iterators::filter
。我想我更喜欢在
next()
上推进源迭代器,而不是在
hasNext()
中,然后存储“next”值或
null
(如果没有可用)。也应该降低一点复杂性。@daniu这对通常的用例
hasNext()
next()
之前被调用并被要求给出正确的答案没有帮助。我可能没有正确解释,这就是我的意思:我刚刚意识到你的解决方案的行为(我的也一样)我不希望出现这样的行为:如果底层集合被修改(如在您的测试用例中),这将导致在更改之前返回的
Iterable
发生更改。甚至在查询第一个元素之前就提前执行可能是一种令人惊讶的行为。但这并不是关于
hasCurrent
字段的差异的原因。我的解决方案只是考虑源代码包含
null
元素并且谓词接受它们的可能性。
hasCurrent
标志用于正确处理该情况。如果排除
null
,也可以在我的解决方案中用
null
测试替换该字段。
Streams.stream(it)
StreamEx.of(it.iterator())
...
stream.filter(pred.negate())
...
return () -> stream.iterator();
return stream::iterator;
static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    return StreamSupport.stream(it.spliterator(), false).filter(pred.negate())::iterator;
}
static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
    Stream<T> stream = stream(it.spliterator(), false);
    Predicate<T> negatedPred = pred.negate();
    Stream<T> filteredStream = stream.filter(negatedPred);
    return filteredStream::iterator;
}
static <T> Iterable<T> select(Iterable<T> toIterate, Predicate<T> pred) {
    return () -> new Iterator<T>() {
        Iterator<T> delegate = toIterate.iterator();
        T next = findNextValid();
        public boolean hasNext() {
            return next != null;
        }
        public T next() {
            if (next == null) throw new NoSuchElementException();
            T result = next;
            next = findNextValid();
            return result;
        }
        private T findNextValid() {
            T result = null;
            while (result == null && delegate.hasNext()) {
                T candidate = delegate.next();
                if (pred.test(candidate)) {
                    result = candidate;
                }
            }
            return result;
        }
    };
}