当前位置: 首页 > 知识库问答 >
问题:

Java -使用流lambdas的多个集合的交集

狄承望
2023-03-14

我有以下功能用于统一多个集合(包括重复元素):

public static <T> List<T> unify(Collection<T>... collections) {
        return Arrays.stream(collections)
               .flatMap(Collection::stream)
               .collect(Collectors.toList()); 
}

如果集合的交集具有类似签名的函数(使用类型相等),那就太好了。例如:

public static <T> List<T> intersect(Collection<T>... collections) {
     //Here is where the magic happens
}

我找到了一个相交函数的实现,但它不使用流:

public static <T> Set<T> intersect(Collection<? extends Collection<T>> collections) {
    Set<T> common = new LinkedHashSet<T>();
    if (!collections.isEmpty()) {
       Iterator<? extends Collection<T>> iterator = collections.iterator();
       common.addAll(iterator.next());
       while (iterator.hasNext()) {
          common.retainAll(iterator.next());
       }
    }
    return common;
}

是否有任何方法可以利用流实现类似于unify函数的功能?我在java8/StreamAPI方面没有太多经验,因为一些建议会非常有用。

共有3个答案

金宣
2023-03-14

我认为使用Set而不是List可能更有意义(可能这是你问题中的一个错别字):

public static <T> Set<T> intersect(Collection<T>... collections) {
     //Here is where the magic happens
     return (Set<T>) Arrays.stream(collections).reduce(
             (a,b) -> {
                 Set<T> c = new HashSet<>(a);
                 c.retainAll(b);
                 return c;
             }).orElseGet(HashSet::new);
}
陶智
2023-03-14

虽然很容易将<code>retainAll

这意味着线性扫描一个集合并测试所有其他集合中的每个元素是否包含将与为每个集合执行retainAll相当。首先迭代最小集合的加分:

public static <T> Set<T> intersect(Collection<? extends Collection<T>> collections) {
    if(collections.isEmpty()) return Collections.emptySet();
    Collection<T> smallest
        = Collections.min(collections, Comparator.comparingInt(Collection::size));
    return smallest.stream().distinct()
        .filter(t -> collections.stream().allMatch(c -> c==smallest || c.contains(t)))
        .collect(Collectors.toSet());
}

或者,可选地

public static <T> Set<T> intersect(Collection<? extends Collection<T>> collections) {
    if(collections.isEmpty()) return Collections.emptySet();
    Collection<T> smallest
        = Collections.min(collections, Comparator.comparingInt(Collection::size));
    HashSet<T> result=new HashSet<>(smallest);
    result.removeIf(t -> collections.stream().anyMatch(c -> c!=smallest&& !c.contains(t)));
    return result;
}
阎咏思
2023-03-14

您可以在一些实用程序类中编写自己的收集器并使用它:

public static <T, S extends Collection<T>> Collector<S, ?, Set<T>> intersecting() {
    class Acc {
        Set<T> result;

        void accept(S s) {
            if(result == null) result = new HashSet<>(s);
            else result.retainAll(s);
        }

        Acc combine(Acc other) {
            if(result == null) return other;
            if(other.result != null) result.retainAll(other.result);
            return this;
        }
    }
    return Collector.of(Acc::new, Acc::accept, Acc::combine, 
                        acc -> acc.result == null ? Collections.emptySet() : acc.result, 
                        Collector.Characteristics.UNORDERED);
}

用法很简单:

Set<T> result = Arrays.stream(collections).collect(MyCollectors.intersecting());

但请注意,收集器不能短路:即使中间结果将是空集合,它仍将处理流的其余部分。

这样的收集器在我免费的StreamEx库中随时可用(参见MoreCollectors.intersecting())。它适用于上述普通流,但如果您将其与StreamEx(扩展正常流)一起使用,则会变短路:处理实际上可能会提前停止。

 类似资料:
  • 问题内容: 我有一套清单: 我要s1∩s2∩s3 … 我可以编写一个函数来执行一系列成对的操作,等等。 有没有推荐,更好或内置的方法? 问题答案: 从python版本2.6开始,您可以对使用多个参数,例如 如果这些集合在列表中,则表示为: 这里是列表扩展 请注意,是 不是 一个静态的方法,但这种使用功能符号应用第一套交叉口列表的其余部分。因此,如果参数列表为空,则将失败。

  • 这是我的密码。我们不允许使用方法或数组,我们只是初学者。我的代码在

  • 由于我不想多次使用流来单独收集每个属性,也不想使用来收集每个属性,是否有任何方法可以用单个流来获得上述属性。

  • 问题内容: 我正在使用python进行游戏,并且能够得到两个列表的交集: 现在,如果一个列表包含和并包含第三个元素,是否有一个内置函数来查找内部所有三个列表的交集?例如 那么结果应该是 问题答案: 对于2.4,您只需定义一个交集函数。 对于较新版本的python: 相交方法接受任意数量的参数 或者,您可以将第一个集合与其自身相交,以避免切片列表并进行复制: 我不太确定哪种方法会更有效,并且感觉这将

  • 这是一个纯粹的概念问题。 Java8中的Lambdas转换为使用调用的方法。 如果一个类可以拥有的方法的最大数量有JVM限制,这是否意味着一个类中使用的lambda的最大数量也受到JVM的严格限制? 这个问题和这个差不多吗?一个Java类最多可以有多少个方法?

  • 问题内容: 我有两个排序集,并且想要进行交集,即。 关于效率,是否有比以下更好的方法: 问题答案: 您应该先使用ZCARD检查哪些元素较少,然后克隆并修剪较短的元素。 其次,您将剩下2个剩菜。您可以重复使用同一辅助程序,以加快清除速度。 我还想建议克隆使用DUMP和RESTORE,但是对于排序集的情况,ZUNIONSTORE实际上要快得多。这是一个100万个元素集的时间安排: