Java 8 - 省略繁琐的收集方法

use*_*192 56 java java-8 java-stream

Java 8 stream api非常好用,我非常喜欢它.让我紧张的一件事是90%的时间我想作为集合输入作为集合和输出.结果是我必须一直打电话stream()collect()方法:

collection.stream().filter(p->p.isCorrect()).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

是否有任何java api可以让我跳过流并直接操作集合(比如linqc#?):

collection.filter(p->p.isCorrect)
Run Code Online (Sandbox Code Playgroud)

man*_*uti 43

是的,使用Collection#removeIf(Predicate):

删除此集合中满足给定谓词的所有元素.

请注意,它将更改给定的集合,而不是返回新的集合.但是您可以创建集合的副本并对其进行修改.另请注意,谓词需要被否定才能充当过滤器:

public static <E> Collection<E> getFilteredCollection(Collection<E> unfiltered,
                                                      Predicate<? super E> filter) {
    List<E> copyList = new ArrayList<>(unfiltered);

    // removeIf takes the negation of filter 
    copyList.removeIf(e -> { return !filter.test(e);});  

    return copyList;
}
Run Code Online (Sandbox Code Playgroud)

但正如@Holger在评论中建议的那样,如果您选择在代码中定义此实用程序方法并在需要获取过滤集合的任何位置使用它,则只需将调用委托给collect该实用程序中的方法.您的来电者代码将更加简洁.

public static <E> Collection<E> getFilteredCollection(Collection<E> unfiltered,
                                                      Predicate<? super E> filter) {
   return unfiltered.stream()
                    .filter(filter)
                    .collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)

  • `copyList.removeIf(filter.negate());` (12认同)
  • 但请记住,谓词被否定(与`filter`相比)并且`removeIf`修改原始集合并且不返回新集合. (10认同)
  • 当您在实用程序方法ala`getFilteredCollection`中隐藏操作时,使用`removeIf`是没有意义的,因为您可以简单地说`return unfiltered.stream().filter(filter).collect(Collectors.toList()); `呼叫者没有区别.并且它比先复制整个列表并在之后删除项目更短*和*更有效. (6认同)
  • @ Jean-FrançoisSavard,这比`collection.stream().filter(p - > p.isCorrect()).collect(toList())`更好吗? (4认同)
  • 可能有趣的是,从Java 9中你可以做`collection.stream().collect(filtering(p - > p.isCorrect(),toList())`.这不是真正的重点[`filtering`](http://download.java.net/java/jdk9/docs/api/java/util/stream/Collectors.html#filtering-java.util.function.Predicate-java.util.stream.收集器 - 收集器,但我认为我宁愿使用它而不是创建方法. (2认同)
  • @shmosel我不会继续讨论为什么我个人觉得它更具可读性,就像那样.我只是想为知识共享指出另一种选择,这在评论中完全没问题.从来没有说过这是一个更好的选择,或者不是. (2认同)
  • @shmosel或许可以满足你对我的第一个评论的一些好奇心,特别是*"这不是过滤收集器的真正意义"*,"过滤"收集器与标准的"过滤器"操作不同.例如,当使用`groupingBy`和`filtering`收集地图时,您可以收集空组,而在使用`Stream#filter`时,它们将被简单地忽略.但可以肯定的是,这与答案和问题无关. (2认同)

Gui*_*Sim 17

您可能喜欢使用StreamEx

StreamEx.of(collection).filter(PClass::isCorrect).toList();
Run Code Online (Sandbox Code Playgroud)

这具有在保持不变性的同时稍微更短暂的优点.

  • 我想提到StreamEx,但在这种情况下它提供的很少. (2认同)
  • 它提供了一个更接近C#API的`stream` API,这可能是OP正在搜索的内容. (2认同)

Jak*_*ski 10

如果您想对藏品进行操作,Guava的FluentIterable是一种方法!

示例(获取10个第一个VIP客户的ID):

FluentIterable
       .from(customers)
       .filter(customer -> customer.isVIP())
       .transform(Client::getId)
       .limit(10);
Run Code Online (Sandbox Code Playgroud)

  • @ user3364192不仅比基于`Stream`的解决方案更好,实际上是_less concise_.但是还需要一个外部库,并且提供的功能远远少于"Stream".我真的看不出这怎么可能是一个更好的解决方案...... (36认同)
  • 这并不比使用Streams好.它返回一个`Iterable`,而不是`Collection`,可以用[`Iterables.filter()`](http://docs.guava-libraries.googlecode.com/git/javadoc/com)更简洁地完成. /google/common/collect/Iterables.html#filter(java.lang.Iterable,%20com.google.common.base.Predicate)). (8认同)
  • @ user3364192如果对`Collectors.*`使用静态导入,则流版本稍微简洁一些. (6认同)
  • 你是对的.但是有一个toList()方法,我发现它比stream的collect(Collectors.toList())更令人愉快. (2认同)

shm*_*sel 7

如果您需要过滤视图而不修改原始集合,请考虑Guava Collections2.filter().

  • @ user3364192请注意,这显然不一样.`filter()`返回一个`view`,所以`Predicate`将在每次迭代/访问时被评估,而`get``size`等变成`O(n)`操作而不是'O(1)`(对于一个` `ArrayList`).这里提出的三种解决方案并不相同 - 它们各有其优点和成本. (5认同)

The*_*Wes 7

Streams有一个定义良好的架构,你可以阅读很多.在开始这条道路之前,您可能想要了解这一点.

但是为什么不实现一个集合,它实现了一个类似的流接口,为您包装该代码?

public class StreamableCollection implements Collection, Stream {
...
}
Run Code Online (Sandbox Code Playgroud)

然后你可以为你的用例做一些棘手的假设.您仍然可以从集合界面打开一个流,但您也可以直接跳入,然后在该句柄的内部打开我想要的流的开头.

    streamableCollection cs = new streamableCollection();
    cs.filter();
    cs.stream();
Run Code Online (Sandbox Code Playgroud)

您的IDE将直接跳到您实现所有内容...只需将所有内容传递回默认实现.

  • 我想你会想要一个定义`stream()`方法的`Streamable`接口,并实现它,而不是`Stream`.它类似于"Iterable"和"Iterator"."流"和"迭代器"都被消耗,而不是可重复使用,所以`*able`接口可以通过为每次再次运行它定义工厂方法来解决这个问题. (2认同)

Ror*_*ter 6

我也认为Stream API很好,但对于短操作来说很冗长.我在一些项目中使用了这些实用程序方法:

import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Functions {

    public static <T,V> List<V> map(final List<T> in, final Function<T, V> function) {
        return in == null ? null : map(in.stream(), function);
    }

    public static <T,V> List<V> map(final Stream<T> in, final Function<T, V> function) {
        return in == null ? null : in
            .map(function)
            .collect(Collectors.toList());
    }

    public static <T> List<T> filter(final List<T> in, final Predicate<T> predicate) {
        return in == null ? null : filter(in.stream(), predicate);
    }

    public static <T> List<T> filter(final Stream<T> in, final Predicate<T> predicate) {
        return in == null ? null : in
            .filter(predicate)
            .collect(Collectors.toList());
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我这样做

List<String> wrapped = Functions.map(myList, each -> "[" + each + "]");
Run Code Online (Sandbox Code Playgroud)

通常我也会静态导入该方法.