Java 是否有方法检查流是否包含所有集合元素?
例如,我需要以下内容:Java 是否有方法检查流是否包含所有集合元素?,java,collections,java-8,java-stream,contains,Java,Collections,Java 8,Java Stream,Contains,例如,我需要以下内容: Collection<String> collection = /* ... */; Stream<Object> stream = /* ... */; boolean containsAll = stream.map(Object::toString).containsAll(collection); Collection=/*…*/; Stream=/*…*/; 布尔containsAll=stream.map(对象::toString.c
Collection<String> collection = /* ... */;
Stream<Object> stream = /* ... */;
boolean containsAll = stream.map(Object::toString).containsAll(collection);
Collection=/*…*/;
Stream=/*…*/;
布尔containsAll=stream.map(对象::toString.containsAll(集合);
当然,我可以使用
collect()
方法和调用Collection.containsAll()
将流中的所有元素累积到另一个集合中,但是如果流太大,处理其所有元素效率低下,该怎么办?无论流有多大,如果它不包含集合的所有元素,则必须处理它的所有元素
如果流
的一个小前缀包含集合
的所有元素,并且集合
比流
小得多,则可以节省处理时间
boolean containsAll =
stream.map(Object::toString)
.filter(s -> collection.contains(s)) // it would be wise to convert collection to a Set
.limit(collection.size())
.count() == collection.size();
请注意,如果流
可能包含集合
的同一元素的多个副本,您可能需要在过滤器()后添加.distinct()
操作,这应该可以做到:
Set<String> set = new HashSet<>(collection);
boolean containsAll = set.isEmpty() || stream.map(Object::toString)
.anyMatch(s -> set.remove(s) && set.isEmpty());
如果集合
可以包含重复项,并且需要检查流
是否包含所有重复项,那么我们需要维护计数器的并发映射
Map<String, AtomicLong> map = new ConcurrentHashMap<>();
collection.forEach(s -> map.computeIfAbsent(s, __ -> new AtomicLong()).incrementAndGet());
boolean containsAll = map.isEmpty() || stream.map(Object::toString)
.filter(map::containsKey)
.filter(s -> map.get(s).decrementAndGet() == 0)
.filter(s -> map.remove(s) != null)
.anyMatch(__ -> map.isEmpty());
Map Map=new ConcurrentHashMap();
collection.forEach->map.computeIfAbsent(s,_u->new AtomicLong()).incrementAndGet();
布尔containsAll=map.isEmpty()| | stream.map(对象::toString)
.filter(映射::containsKey)
.filter->map.get.decrementAndGet()=0
.filter->map.remove!=null)
.anyMatch(_->map.isEmpty());
代码略有变化,但想法是一样的
boolean allMatch = stream.map(Object::toString)
.allMatch(s -> collection.contains(s));
allMatch方法的工作原理与anyMatch类似,但将检查流的所有元素是否与给定谓词匹配。从集合创建一个集合,以加快搜索操作O(1)
另一种方法是通过不包含在集合中进行过滤,并使用限制(1)
进行优化
boolean isContains = stream.map(Object::toString)
.filter(s -> !set.contains(s))
.limit(1)
.count() > 0;
就我个人而言,我认为使用settemp=source.stream().map(Object::toString.collect(toSet());布尔containsAll=临时containsAll(集合)代码>@OusmaneD。如果流像OP假设的那样太大怎么办?想象一下,当流
是惰性生成的,并且不是所有元素都持久存在内存中时的情况。例如,使用文件::行
您可以处理大型文件,即使它们无法放入您的内存。在这种情况下,收集到集合中会导致OutOfMemoryError
。在这种情况下,不使用collect
比任何其他操作(count
)的好处我真的不太明白。由于对完整元素的流处理无论如何都会发生(最坏的情况),是否有可能进行优化以避免占用那么多内存?@Naman如果流有1000000个元素,但集合的所有元素都出现在流的前50个元素中,它会处理所有元素吗?当然,如果不是集合的所有元素都出现在流中(或者如果最后一个元素出现在流的末尾附近),这个解决方案将不会有帮助?如果对流和集合进行排序,处理containsAll会更快吗?@Eran By containsAll我指的是您的方法,查找集合是否包含流的所有元素。谢谢你的澄清。@Paul我误解了你的问题。如果对流和集合进行了排序,那么即使流不包含集合的所有元素,也应该能够生成一个有效的解决方案(不处理整个流),因为我们可以确定流中的某个点,在该点之后不再有属于集合的元素。这可以通过takeWhile
完成。似乎您还可以将其包装在列表中
以支持检查流是否包含元素的多个实例<代码>列表。删除
将返回true,直到删除所有副本。@SeanVanGorder实际上,这是一个非常有趣的案例。为了简单起见,我在解决方案中忽略了这一点。但是使用List
我们将失去HashSet
提供的性能提升。在这种情况下,您只需检查原始集合是否是Set
并将其包装在HashSet
中,而不是ArrayList
。这种映射计数预处理似乎是一种折衷,对于大型非Set
集合可能更快,但对于小型集合可能更糟。这是有道理的。编程是一种权衡。不同的数据结构适用于不同的情况。软件工程师仍然需要选择一个更适合他/她试图解决的任务的软件。这让我大吃一惊。我从没想过如果像这样使用流。谢谢。来自一名大学生这并没有正确回答问题,它检查流的所有元素是否都包含在集合中,问题询问集合的所有元素是否都在流中。
Set<String> set = new HashSet<>(collection);
boolean containsAll = stream.map(Object::toString)
.allMatch(s -> set.contains(s));
boolean isContains = stream.map(Object::toString)
.filter(s -> !set.contains(s))
.limit(1)
.count() > 0;