Convertir matriz de Java a Iterable

150

Tengo una matriz de primitivas, por ejemplo para int, int [] foo. Puede ser pequeño o no.

int foo[] = {1,2,3,4,5,6,7,8,9,0};

¿Cuál es la mejor manera de crear un a Iterable<Integer>partir de él?

Iterable<Integer> fooBar = convert(foo);

Notas:

No responda con bucles (a menos que pueda dar una buena explicación sobre cómo el compilador hace algo inteligente al respecto).

También tenga en cuenta que

int a[] = {1,2,3};
List<Integer> l = Arrays.asList(a);

Ni siquiera se compilará

Type mismatch: cannot convert from List<int[]> to List<Integer>

Compruebe también ¿Por qué una matriz no es asignable a Iterable? antes de contestar

Además, si usa alguna biblioteca (por ejemplo, Guava), explique por qué es la mejor. (Porque es de Google no es una respuesta completa: P)

Por último, dado que parece haber una tarea al respecto, evite publicar un código de tarea.

ntg
fuente
posible duplicado de Iterator para array
NPE
Agréguelos a un LinkedList y luego simplemente devuelva el iterador de ese conjunto.

Respuestas:

117
Integer foo[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

List<Integer> list = Arrays.asList(foo);
// or
Iterable<Integer> iterable = Arrays.asList(foo);

Aunque necesita usar una Integermatriz (no una intmatriz) para que esto funcione.

Para las primitivas, puedes usar guayaba:

Iterable<Integer> fooBar = Ints.asList(foo);
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>15.0</version>
    <type>jar</type>
</dependency>

Para Java8: (de la respuesta de Jin Kwon)

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();
fmucar
fuente
10
Dos notas: 1) él tiene int, no Integer2) Listya está, Iterableasí que la tercera línea no tiene sentido.
maksimov
1
necesita Iterable por eso hay una tercera línea.
fmucar
55
Las líneas 2 y 3 son opciones, diría :)
fmucar
1
Esto no es parte de una tarea, solo estaba tratando de evitar la duplicación de código para una función de depuración que procesa el contenido de una matriz o lista ... Mientras miraba, encontré Arrays.asList (..) ;, pero al menos Eclipse parece pensar que no hará lo que quiero (por ejemplo, infiere el resultado de Arrays.asList (foo) como List <int []>, no List <Integer> ...) Me pareció lo suficientemente interesante como para pregunta ... (-Rompiendo comentario en partes causa de límites-)
ntg
1
En general, uno podría pensar en muchas maneras de hacerlo, pero me preguntaba cuál es el MEJOR (por ejemplo, un bucle sería más lento en comparación con ... {bueno, ¡el problema es que no puedo pensar en nada !: )}) También consulte stackoverflow.com/questions/1160081/… para una discusión sobre por qué, aunque mi pregunta no es por qué, sino cómo y qué tipo de contenedor sería mejor (¿por qué ArrayList? De hecho, podría imaginarme una AbstractList wrapper usando Generics .., Probablemente depende del tamaño ...)
ntg
44

solo mis 2 centavos:

final int a[] = {1,2,3};

java.lang.Iterable<Integer> aIterable=new Iterable<Integer>() {

    public Iterator<Integer> iterator() {
       return new Iterator<Integer>() {
            private int pos=0;

            public boolean hasNext() {
               return a.length>pos;
            }

            public Integer next() {
               return a[pos++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Cannot remove an element of an array.");
            }
        };
    }
};
Joerg Ruethschilling
fuente
9
remove () no es necesario en java 8, porque es un método predeterminado que genera UnsupportedOperationException. Solo si desea proporcionar un mejor mensaje de explicación.
Alex
+1 Hago algo similar para crear un Iterator<Character>de a String. Implementar el suyo Iteratorparece ser la única forma de evitar iterar innecesariamente a través de todos los valores para convertir del tipo de objeto al tipo primitivo (a través de Guava's, Ints.asList()por ejemplo), solo para poder obtener uno Iteratordel Listque se creó.
spaaarky21
2
Tienes razón Alex. Se agregaron métodos predeterminados a Java 8. En 2013 agregué este antiguo código aquí.
Joerg Ruethschilling
29

Con Java 8, puedes hacer esto.

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();
Jin Kwon
fuente
20

Guava proporciona el adaptador que desea como Int.asList () . Hay un equivalente para cada tipo primitivo en la clase asociada, por ejemplo, Booleanspara boolean, etc.

int foo[] = {1,2,3,4,5,6,7,8,9,0};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

Las sugerencias anteriores para usar Arrays.asListno funcionarán, incluso si se compilan porque obtienes una en Iterator<int[]>lugar de una Iterator<Integer>. Lo que sucede es que, en lugar de crear una lista respaldada por su matriz, creó una lista de matrices de 1 elemento que contiene su matriz.

BeeOnRope
fuente
solo una nota: el enlace ya no funciona. Enlace de Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle
Gracias @Passi, corregido (parece que ya no puedo encontrar una forma compatible con Google para vincular a javadoc, así que me vinculé a la fuente que proporcionó).
BeeOnRope
8

Tuve el mismo problema y lo resolví así:

final YourType[] yourArray = ...;
return new Iterable<YourType>() {
  public Iterator<YourType> iterator() {
     return Iterators.forArray(yourArray);   // Iterators is a Google guava utility
  }
}

El iterador en sí es un vago, UnmodifiableIteratorpero eso es exactamente lo que necesitaba.

mindas
fuente
7

En Java 8 o posterior, Iterablese devuelve una interfaz funcional Iterator. Entonces puedes hacer esto.

int[] array = {1, 2, 3};
Iterable<Integer> iterable = () -> Arrays.stream(array).iterator();
for (int i : iterable)
    System.out.println(i);

->

1
2
3
saka1029
fuente
3

En primer lugar, solo puedo estar de acuerdo en que Arrays.asList(T...)es claramente la mejor solución para los tipos de envoltorios o matrices con tipos de datos no primitivos. Este método llama a un constructor de una AbstractListimplementación estática privada simple en la Arraysclase que básicamente guarda la referencia de matriz dada como campo y simula una lista anulando los métodos necesarios.

Si puede elegir entre un tipo primitivo o un tipo Wrapper para su matriz, usaría el tipo Wrapper para tales situaciones, pero, por supuesto, no siempre es útil o obligatorio. Solo habría dos posibilidades que puede hacer:

1) Puede crear una clase con un método estático para cada matriz de tipo de datos primitiva ( boolean, byte, short, int, long, char, float, doubledevolviendo un Iterable<WrapperType >. Estos métodos usarían clases anónimas deIterator (además deIterable) que pueden contener la referencia del argumento del método que comprende (por ejemplo, un int[]) como campo para implementar los métodos.

-> Este enfoque es eficaz y le ahorra memoria (a excepción de la memoria de los métodos recién creados, aunque el uso Arrays.asList()tomaría la memoria de la misma manera)

2) Dado que las matrices no tienen métodos (como para leer al lado vincularon) tampoco pueden proporcionar una Iteratorinstancia. Si realmente es demasiado vago para escribir nuevas clases, debe usar una instancia de una clase ya existente que se implemente Iterableporque no hay otra forma de instanciar la clase de implementación cuyo constructor permite una matriz de tipo primitiva (porqueIterable o un subtipo.
La ÚNICA forma de crear una implementación derivada de Colección existenteIterablees usar un bucle (excepto que usa clases anónimas como se describió anteriormente) o crea una instancia de una no permite matrices con elementos de tipo primitivo) pero, que yo sepa, la API de Java no presenta una clase como esa. La razón del bucle puede explicarse fácilmente: para cada Colección necesita Objetos y los tipos de datos primitivos no son objetos. Los objetos son mucho más grandes que los tipos primitivos, por lo que requieren datos adicionales que deben generarse para cada elemento de la matriz de tipos primitivos. Eso significa que si dos formas de tres (usando o usando una Colección existente) requieren un agregado de objetos, debe crear para cada valor primitivo de suIterableObject[]


Arrays.asList(T...)int[] matriz, el objeto contenedor. La tercera forma usaría la matriz tal como está y la usaría en una clase anónima, ya que creo que es preferible debido al rendimiento rápido.

También hay una tercera estrategia que usa un Objectargumento como para el método donde desea usar la matriz oIterabley requeriría verificaciones de tipo para determinar qué tipo tiene el argumento, sin embargo, no lo recomendaría en absoluto, ya que generalmente debe tener en cuenta que el Objeto no siempre tiene el tipo requerido y que necesita un código separado para ciertos casos.

En conclusión, es culpa del problemático sistema de tipo genérico de Java que no permite usar tipos primitivos como tipo genérico, lo que ahorraría mucho código al usar simplemente Arrays.asList(T...). Por lo tanto, necesita programar para cada tipo de matriz primitiva, necesita un método de este tipo (que básicamente no hace ninguna diferencia en la memoria utilizada por un programa C ++ que crearía para cada argumento de tipo utilizado un método separado.

CodingVirus01
fuente
3

Puedes usar IterableOfdesde Cactoos :

Iterable<String> names = new IterableOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

Luego, puede convertirlo en una lista usando ListOf:

List<String> names = new ListOf<>(
  new IterableOf<>(
    "Scott Fitzgerald", "Fyodor Dostoyevsky"
  )
);

O simplemente esto:

List<String> names = new ListOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);
yegor256
fuente
1

Si bien ya se ha publicado una respuesta similar, creo que la razón para usar el nuevo PrimitiveIterator.OfInt no estaba clara. Una buena solución es usar Java 8 PrimitiveIterator ya que está especializado para tipos int primitivos (y evita la penalización adicional de boxing / unboxing):

    int[] arr = {1,2,3};
    // If you use Iterator<Integer> here as type then you can't get the actual benefit of being able to use nextInt() later
    PrimitiveIterator.OfInt iterator = Arrays.stream(arr).iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.nextInt());
        // Use nextInt() instead of next() here to avoid extra boxing penalty
    }

Ref: https://doc.bccnsoft.com/docs/jdk8u12-docs/api/java/util/PrimitiveIterator.OfInt.html

Binod Pant
fuente
-2

En java8 IntSteam stream puede ser encajonado a stream de Integers.

public static Iterable<Integer> toIterable(int[] ints) {
    return IntStream.of(ints).boxed().collect(Collectors.toList());
}

Creo que el rendimiento importa en función del tamaño de la matriz.

Soumyakanta Mohapatra
fuente