繁体   English   中英

使用Java流删除和收集元素

[英]Remove and collect elements with Java streams

假设我有一个Collection和一个Predicate ,它匹配我想从Collection删除的元素。 但我不只是想丢弃它们,我想将匹配的元素移动到一个新的集合中。 我会做一些像这样在Java 7中:

List<E> removed = new LinkedList<>();
for (Iterator<E> i = data.iterator(); i.hasNext();) {
    E e = i.next();
    if (predicate.test(e)) {
        removed.add(e);
        i.remove();
    }
}

我很好奇是否有流/ Java 8方法可以做到这一点。 Collections.removeIf()遗憾的是简单地返回一个boolean (甚至不包括被删除元素的数量?太糟糕了。)我想象这样的东西(虽然当然.removeAndYield(Predicate)不存在):

List<E> removed = data.removeAndYield(predicate).collect(Collectors.toList());

注意:这个问题的灵感来自类似的问题 ; 这个问题是关于从集合中删除项目获取流的更一般情况。 正如在链接问题中指出的那样,必要的解决方案可能更具可读性,但我很好奇是否甚至可以使用流。

编辑:显然,我们可以将任务分成两个单独的步骤,并假设适当的数据结构,它将是有效的。 问题是可以在任意集合上完成(可能没有高效的.contains()等)。

如果你不介意,让我稍微弯曲你的要求。 :-)

期望结果的一个特征是匹配元素应该在一个集合中结束,并且非匹配元素应该最终在不同的集合中。 在Java-8之前的变异世界中,考虑获取非匹配元素集合的最简单方法是从原始集合中删除匹配元素。

但是删除 - 修改原始列表 - 需求的内在部分?

如果不是,则可以通过简单的分区操作来实现结果:

Map<Boolean, List<E>> map = data.stream().collect(partitioningBy(predicate));

结果映射本质上是两个列表,其中包含匹配(key = true)和非匹配(key = false)元素。

优点是该技术可以在一次通过中并且在必要时并行完成。 当然,与从原始匹配中删除匹配相比,这会创建一个重复的非匹配元素列表,但这是为不变性付出的代价。 权衡可能是值得的。

我会保持简单:

Set<E> removed = set.stream()
    .filter(predicate)
    .collect(Collectors.toSet());

set.removeAll(removed);

如果您想要一种功能性的方法来执行此操作,您可以编写自己的方法。

static <E> Set<E> removeIf(Collection<? extends E> collection, Predicate<? super E> predicate) {
    Set<E> removed = new HashSet<>();
    for (Iterator<? extends E> i = collection.iterator(); i.hasNext();) {
        E e = i.next();
        if (predicate.test(e)) {
            removed.add(e);
            i.remove();
        }
    }
    return removed;
}

这可用于从List删除所有奇数。

Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Set<Integer> removed = removeIf(set, i -> i % 2 != 0);
System.out.println(set);
System.out.println(removed);

只要写一个像这样的可重用函数:

/**
 * Removes all matching element from iterator
 * 
 * @param it
 * @param predicate
 */
public static <E> void removeMatching(final Iterator<E> it, final Predicate<E> predicate) {
    while (it.hasNext()) {
        final E e = it.next();
        if (predicate.test(e)) {
            it.remove();
        }
    }
}

我还没有找到Streams的预先存在的解决方案。 使用iterator.remove()比临时集需要更少的内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM