the questions interview interfaces framework example characteristics java collections

questions - list java



Manera fácil de convertir Iterable a Colección (16)

Aquí hay un SSCCE para una gran manera de hacer esto en Java 8

import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.stream.Collectors; import java.util.stream.IntStream; public class IterableToCollection { public interface CollectionFactory <T, U extends Collection<T>> { U createCollection(); } public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) { U collection = factory.createCollection(); iterable.forEach(collection::add); return collection; } public static void main(String[] args) { Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList()); ArrayList<Integer> arrayList = collect(iterable, ArrayList::new); HashSet<Integer> hashSet = collect(iterable, HashSet::new); LinkedList<Integer> linkedList = collect(iterable, LinkedList::new); } }

En mi aplicación, uso una biblioteca de terceros (Spring Data para MongoDB para ser exactos).

Los métodos de esta biblioteca devuelven Iterable<T> , mientras que el resto de mi código espera la Collection<T> .

¿Hay algún método de utilidad en algún lugar que me permita convertir rápidamente uno a otro? Me gustaría evitar crear un montón de bucles foreach en mi código para una cosa tan simple.


Con Guava puedes usar Lists.newArrayList(Iterable) o Sets.newHashSet(Iterable) , entre otros métodos similares. Esto, por supuesto, copiará todos los elementos en la memoria. Si eso no es aceptable, creo que su código que funciona con estos debería tomar Iterable lugar de Collection . La guayaba también proporciona métodos convenientes para hacer cosas que puedes hacer en una Collection usando un Iterable (como Iterables.isEmpty(Iterable) o Iterables.contains(Iterable, Object) ), pero las implicaciones de rendimiento son más obvias.


Desde CollectionUtils :

List<T> targetCollection = new ArrayList<T>(); CollectionUtils.addAll(targetCollection, iterable.iterator())

Aquí están las fuentes completas de este método de utilidad:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) { while (iterator.hasNext()) { collection.add(iterator.next()); } }


Dos observaciones

  1. No es necesario convertir Iterable a Collection para usar foreach loop. Iterable se puede usar en dicho loop directamente, no hay una diferencia sintáctica, por lo que apenas entiendo por qué se hizo la pregunta original.
  2. La forma sugerida de convertir Iterable a Collection es insegura (lo mismo se relaciona con CollectionUtils): no hay garantía de que las llamadas posteriores al método next () devuelvan diferentes instancias de objetos. Además, esta preocupación no es pura teórica. Por ejemplo, la implementación iterable utilizada para pasar valores a un método de reducción de Hadoop Reducer siempre devuelve la misma instancia de valor, solo que con valores de campo diferentes. Por lo tanto, si aplica makeCollection desde arriba (o CollectionUtils.addAll (Iterator)), terminará con una colección con todos los elementos idénticos.

En JDK 8, sin depender de libs adicionales:

Iterator<T> source = ...; List<T> target = new ArrayList<>(); source.forEachRemaining(target::add);

Edición: La anterior es para Iterator . Si estás tratando con Iterable ,

iterable.forEach(target::add);


En Java 8, puede hacer esto para agregar todos los elementos de Iterable to Collection y devolverlos:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) { Collection<T> collection = new ArrayList<>(); iterable.forEach(collection::add); return collection; }

Inspirado en la respuesta de @Freys.


Esta no es una respuesta a su pregunta, pero creo que es la solución a su problema. La interfaz org.springframework.data.repository.CrudRepository sí tiene métodos que devuelven java.lang.Iterable pero no debe usar esta interfaz. En su lugar, use sub interfaces, en su caso org.springframework.data.mongodb.repository.MongoRepository . Esta interfaz tiene métodos que devuelven objetos de tipo java.util.List .


Mientras lo hace, no olvide que todas las colecciones son finitas, mientras que Iterable no tiene promesas de ningún tipo. Si algo es iterable, puedes obtener un iterador y eso es todo.

for (piece : sthIterable){ .......... }

se ampliará a:

Iterator it = sthIterable.iterator(); while (it.hasNext()){ piece = it.next(); .......... }

No es necesario que it.hasNext () devuelva falso. Por lo tanto, en el caso general, no puede esperar poder convertir cada Iterable en una Colección. Por ejemplo, puede iterar sobre todos los números naturales positivos, iterar sobre algo con ciclos que produzcan los mismos resultados una y otra vez, etc.

De lo contrario: la respuesta de Atrey es bastante buena.


Prueba StickyList de Cactoos :

List<String> list = new StickyList<>(iterable);


Solución concisa con Java 8 usando java.util.stream :

public static <T> List<T> toList(final Iterable<T> iterable) { return StreamSupport.stream(iterable.spliterator(), false) .collect(Collectors.toList()); }


También puede escribir su propio método de utilidad para esto:

public static <E> Collection<E> makeCollection(Iterable<E> iter) { Collection<E> list = new ArrayList<E>(); for (E item : iter) { list.add(item); } return list; }


Tan pronto como llame, contains , containsAll hashCode , equals , hashCode , remove , retainAll , size o toArray , tendría que atravesar los elementos de todos modos.

Si ocasionalmente solo llamas a métodos como isEmpty o clear , supongo que serías mejor creando la colección perezosamente. Por ejemplo, podría tener un ArrayList respaldo para almacenar elementos previamente iterados.

No conozco ninguna clase de este tipo en ninguna biblioteca, pero debería ser un ejercicio bastante simple para escribir.


Uso mucho FluentIterable.from(myIterable).toList() .


Utilizo mi utilidad personalizada para lanzar una colección existente si está disponible.

Principal:

public static <T> Collection<T> toCollection(Iterable<T> iterable) { if (iterable instanceof Collection) { return (Collection<T>) iterable; } else { return Lists.newArrayList(iterable); } }

Idealmente, lo anterior usaría ImmutableList, pero ImmutableCollection no permite valores nulos que pueden proporcionar resultados indeseables.

Pruebas:

@Test public void testToCollectionAlreadyCollection() { ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST); assertSame("no need to change, just cast", list, toCollection(list)); } @Test public void testIterableToCollection() { final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST); Collection<String> collection = toCollection(new Iterable<String>() { @Override public Iterator<String> iterator() { return expected.iterator(); } }); assertNotSame("a new list must have been created", expected, collection); assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection)); }

Implemento utilidades similares para todos los subtipos de Colecciones (Conjunto, Lista, etc.). Pensaría que esto ya sería parte de Guava, pero no lo he encontrado.


Ya que RxJava es un martillo y esta clase de clavo, puedes hacerlo.

Observable.from(iterable).toList().toBlocking().single();


IteratorUtils de commons-collections puede ayudar (aunque no son compatibles con los genéricos en la última versión estable 3.2.1):

@SuppressWarnings("unchecked") Collection<Type> list = IteratorUtils.toList(iterable.iterator());

La versión 4.0 (que se encuentra en SNAPSHOT en este momento) admite genéricos y puede deshacerse de @SuppressWarnings .

Actualización: Verifique IterableAsList desde Cactoos .