如何根据谓词过滤Iterable?

时间:2019-05-29 17:14:53

标签: java java-8 predicate iterable

我想使用Iterable<String>和谓词来选择要保留的字符串,以进行字符串列表过滤,其他字符串必须从列表中删除,但是我并没有低估我如何进行删除。

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",""}

我希望

{"a","b"}

4 个答案:

答案 0 :(得分:3)

import com.sourcesense.sisal.socialbetting.dev.example.elastic.service.QuarkusElasticService; import io.quarkus.vertx.ConsumeEvent; import io.reactiverse.elasticsearch.client.RestHighLevelClient; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import org.apache.http.HttpHost; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; public class QuarkusElasticServiceImpl implements QuarkusElasticService { @Inject Vertx vertx; private RestHighLevelClient esClient; @PostConstruct public void init() { RestClientBuilder builder = RestClient.builder( new HttpHost("localhost", 9200, "http"), new HttpHost("localhost", 9201, "http")); esClient = RestHighLevelClient.create(vertx, builder); } @Override @ConsumeEvent("QuarkusElasticService.getReq") public CompletionStage<JsonObject> getReq(JsonObject jsonObject) { CompletableFuture future = new CompletableFuture(); GetRequest getRequest = new GetRequest( jsonObject.getString("index"), jsonObject.getString("id")); esClient.getAsync(getRequest, RequestOptions.DEFAULT, ar -> { if (ar.failed()) { future.completeExceptionally(new Exception("erroraccio")); } else { future.complete(JsonObject.mapFrom(ar.result())); } }); return future; } } 代表根据请求提供Iterable的能力。因此,要使用过滤逻辑装饰现有的可迭代对象,必须实现装饰Iterator

Iterator

您可以通过

进行测试
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;
        }
    };
}

实现这样的List<String> original = new ArrayList<>(); Collections.addAll(original, "foo", "bar", "baz"); Iterable<String> filter = select(original, s -> s.startsWith("b")); System.out.println(String.join(", ", filter)); original.removeIf(s -> !s.endsWith("r")); System.out.println(String.join(", ", filter)); 时,最大的挑战是为IteratorhasNext这两个方法提供正确的语义,而又不保证调用者如何调用它们,即您不能假定它将永远不会调用next两次,也不能假定总是用前面的hasNext()来调用next()

使用Stream API可以更轻松地实现相同的逻辑:

hasNext()

答案 1 :(得分:1)

由于任何CollectionIterable,只需将符合条件的项目添加到新集合中,然后再返回即可:

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;
}

很少有见解:

  • pred.test(s)==false表达式应简化为!pred.test(s)
  • 可以使用来缩短方法的全部内容:

    static <T> Iterable<T> select(Iterable<T> it, Predicate<T> pred) {
        return StreamSupport.stream(it.spliterator(), false)
            .filter(pred)
            .collect(Collectors.toList());
    }
    

答案 2 :(得分:0)

首先将您的Iterable<T>包裹到Stream<T>中:

  • 普通Java:

    StreamSupport.stream(it.spliterator(), false)
    
  • Guava

    Streams.stream(it)
    
  • StreamEx

    StreamEx.of(it.iterator())
    

然后通过您的Predicate<T>对其进行过滤:

...
stream.filter(pred.negate())
...

最后返回Iterable<T>

  • lambda

    return () -> stream.iterator();
    
  • method reference

    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;
}

答案 3 :(得分:0)

我在评论中表示的Holger替代解决方案如下:

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;
        }
    };
}

不同之处在于,hasCurrent不需要额外的标记,并且它在实际请求下一个元素之前使Iterator前进。您可能会认为后者是不可取的。