Nolequen:
Por ejemplo, necesito algo como:
Collection<String> collection = /* ... */;
Stream<Object> stream = /* ... */;
boolean containsAll = stream.map(Object::toString).containsAll(collection);
Por supuesto, podría acumular todos los elementos de la corriente a otra Collection
utilizando collect()
el método y la llamada Collection.containsAll()
, pero lo que si la corriente es demasiado grande y es ineficiente para procesar todos sus elementos?
ETO:
Esto debería funcionar:
Set<String> set = new HashSet<>(collection);
boolean containsAll = set.isEmpty() || stream.map(Object::toString)
.anyMatch(s -> set.remove(s) && set.isEmpty());
La solución podría parecer confuso, pero la idea es sencilla:
- Con el fin de evitar múltiples iteraciones sobre
collection
la envolvemos en unaHashSet
. (En caso de que sustream
es uno paralelo, entonces usted tendrá que utilizar un conjunto de hash concurrente. Ver este post para más detalles) - Si el
collection
(oset
) está vacía, entonces volvemostrue
sin procesar elstream
- Para cada entrada del
stream
tratamos de sacarlo deset
. En caso de que el resultado deSet::remove
estrue
(por lo tanto, fue contenido porset
) y elset
está vacía después de la extracción, se puede concluir questream
contenía todos los elementos de la inicialcollection
. - El funcionamiento del terminal
Stream::anyMatch
es un cortocircuito en uno. Por lo tanto, se detendrá la iteración más destream
una vez que elset
usuario está vacía. En peor de los casos vamos a procesar toda la corriente.
Quizás esta es una forma poco más legible:
Set<String> set = new HashSet<>(collection);
boolean containsAll = set.isEmpty() || stream.map(Object::toString)
.filter(set::remove)
.anyMatch(__ -> set.isEmpty());
Si el collection
puede contener duplicados y no es un requisito para comprobar si stream
contiene todos ellos, entonces necesitaremos para mantener un mapa concurrente de contadores.
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());
El código modificado ligeramente, pero la idea es la misma.