Conversão Java Array para Iterable

150

Eu tenho uma matriz de primitivas, por exemplo, para int, int [] foo. Pode ser de tamanho pequeno ou não.

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

Qual é a melhor maneira de criar um a Iterable<Integer>partir dele?

Iterable<Integer> fooBar = convert(foo);

Notas:

Por favor, não responda usando loops (a menos que você possa dar uma boa explicação sobre como o compilador faz algo inteligente sobre eles?)

Observe também que

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

Não vai nem compilar

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

Verifique também Por que uma matriz não é atribuível à Iterable? antes de responder.

Além disso, se você usa alguma biblioteca (por exemplo, Goiaba), explique por que esse é o melhor. (Como é do Google não é uma resposta completa: P)

Por fim, como parece haver um dever de casa sobre isso, evite postar código de dever de casa.

ntg
fonte
possível duplicado de Repetidor por matriz
NPE
Adicione-os a um LinkedList e retorne o iterador desse conjunto.

Respostas:

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);

Embora você precise usar uma Integermatriz (não uma intmatriz) para que isso funcione.

Para primitivos, você pode usar goiaba:

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: (da resposta 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
fonte
10
Duas notas: 1) ele tem int, não Integer2) Listjá é, Iterableportanto, a terceira linha é inútil.
Maksimov
1
ele precisa da Iterable por que há uma terceira linha.
Fevento 26/04
5
2ª e 3ª linhas são opções gostaria de dizer :)
fmucar
1
Isso não faz parte de uma lição de casa, eu estava apenas tentando evitar a duplicação de código para uma função de depuração processando o conteúdo de uma matriz ou lista ... Enquanto olhava ao redor, encontrei Arrays.asList (..) ;, mas pelo menos O Eclipse parece pensar que não fará o que eu quero (por exemplo, infere o resultado de Arrays.asList (foo) como uma lista <int []>, não na lista <Integer> ...). Achei isso interessante o suficiente para um pergunta ... (-Breaking comentário em partes causar de limits-)
NTG
1
Em geral, é possível pensar em várias maneiras de fazê-lo, mas eu queria saber qual é o MELHOR (por exemplo, um loop seria muito mais lento em comparação com ... {bem, o problema é que não consigo pensar em nada !: )}) Verifique também stackoverflow.com/questions/1160081/… para obter uma discussão sobre o motivo, mas minha pergunta não é por que, mas como e que tipo de contêiner seria melhor (por que ArrayList? Na verdade, eu poderia imaginar alguns AbstractList wrapper utilizando Generics .. Provavelmente depende do tamanho ...)
NTG
44

apenas meus 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
fonte
9
remove () não é necessário no java 8, porque é um método padrão que lança UnsupportedOperationException. Somente se você desejar fornecer uma mensagem de explicação melhor.
Alex
+1 Eu faço algo semelhante para criar um Iterator<Character>de a String. Implementar o seu próprio Iteratorparece ser a única maneira de evitar iterações desnecessárias através de todos os valores para converter do tipo de objeto para o tipo primitivo (via Guava's, Ints.asList()por exemplo), apenas para obter um resultado Iteratordo Listque foi criado.
precisa saber é o seguinte
2
Você está certo, Alex. Os métodos padrão foram adicionados ao Java 8. Em 2013, adicionei este antigo pedaço de código aqui.
Joerg Ruethschilling
29

Com o Java 8, você pode fazer isso.

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
fonte
20

A goiaba fornece o adaptador que você deseja como Int.asList () . Existe um equivalente para cada tipo primitivo na classe associada, por exemplo, 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);
}

As sugestões acima a serem usadas Arrays.asListnão funcionarão, mesmo que elas sejam compiladas porque você recebe um Iterator<int[]>e não Iterator<Integer>. O que acontece é que, em vez de criar uma lista apoiada por sua matriz, você criou uma lista de matrizes com 1 elemento, contendo sua matriz.

BeeOnRope
fonte
apenas uma observação: o link não está mais funcionando. Link do Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle em
Obrigado @Passi, corrigido (parece que não consigo mais encontrar uma maneira suportada pelo Google para vincular ao javadoc, por isso vinculei à fonte que você forneceu).
BeeOnRope
8

Eu tive o mesmo problema e resolvi-o assim:

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

O iterador em si é um preguiçoso, UnmodifiableIteratormas é exatamente isso que eu precisava.

mindas
fonte
7

No Java 8 ou posterior, Iterableé retornada uma interface funcional Iterator. Então você pode fazer isso.

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

->

1
2
3
saka1029
fonte
3

Antes de tudo, só posso concordar que essa Arrays.asList(T...)é claramente a melhor solução para tipos ou matrizes de wrapper com tipos de dados não primitivos. Esse método chama um construtor de uma AbstractListimplementação estática privada simples na Arraysclasse que basicamente salva a referência da matriz especificada como campo e simula uma lista substituindo os métodos necessários.

Se você pode escolher entre um tipo primitivo ou um tipo de invólucro para sua matriz, eu usaria o tipo de invólucro para essas situações, mas é claro que nem sempre é útil ou necessário. Existem apenas duas possibilidades que você pode fazer:

1) Você pode criar uma classe com um método estático para cada matriz de tipo de dados primitiva ( boolean, byte, short, int, long, char, float, doubleretornando um Iterable<WrapperType >. Esses métodos usariam classes anônimas de Iterator(além deIterable) que têm permissão para conter a referência do argumento do método compreensivo (por exemplo, um int[]) como campo para implementar os métodos.

-> Essa abordagem é de bom desempenho e economiza memória (exceto para a memória dos métodos recém-criados, embora o uso Arrays.asList()levaria memória da mesma maneira)

2) Como as matrizes não têm métodos (como lidas na lateral) você vinculou) eles também não podem fornecer uma Iteratorinstância. Se você realmente tem preguiça de escrever novas classes, deve usar uma instância de uma classe já existente que implemente Iterableporque não há outra maneira de instanciar Iterableou um subtipo.
A ÚNICA maneira de criar um derivado de coleção existente implementandoIterableé usar um loop (exceto se você usar classes anônimas como descrito acima) ou instanciar uma Iterableclasse de implementação cujo construtor permita uma matriz de tipos primitivos (porque Object[]não permite matrizes com elementos de tipos primitivos), mas até onde eu sei, a API Java não apresenta uma classe como essa.

O motivo do loop pode ser explicado facilmente:
para cada coleção, você precisa de objetos e tipos de dados primitivos não são objetos. Os objetos são muito maiores que os tipos primitivos, de modo que requerem dados adicionais que devem ser gerados para cada elemento da matriz de tipos primitivos. Isso significa que, se duas maneiras de três (usar Arrays.asList(T...)ou usar uma coleção existente) exigirem um agregado de objetos, você precisará criar para cada valor primitivo do seuint[]matriz o objeto wrapper. A terceira maneira usaria a matriz como está e a utilizaria em uma classe anônima, pois acho que é preferível devido ao desempenho rápido.

Há também uma terceira estratégia usando um Objectargumento as para o método em que você deseja usar a matriz ou Iterablee isso exigiria verificações de tipo para descobrir qual o tipo de argumento, no entanto, eu não o recomendaria, pois você geralmente precisa considere que o objeto nem sempre tem o tipo necessário e que você precisa de código separado para determinados casos.

Concluindo, a culpa é do sistema problemático de tipo genérico do Java, que não permite usar tipos primitivos como tipo genérico, o que economizaria muito código usando simplesmenteArrays.asList(T...). Portanto, você precisa programar para cada matriz de tipos primitivos, um método desse tipo (que basicamente não faz diferença para a memória usada por um programa C ++ que criaria para cada argumento de tipo usado um método separado.

CodingVirus01
fonte
3

Você pode usar IterableOfdo Cactoos :

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

Em seguida, você pode transformá-lo em uma lista usando ListOf:

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

Ou simplesmente isso:

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

Embora uma resposta semelhante já tenha sido publicada, acho que o motivo para usar o novo PrimitiveIterator.OfInt não estava claro. Uma boa solução é usar o Java 8 PrimitiveIterator, pois é especializado em tipos int primitivos (e evita a penalidade extra de boxe / 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
fonte
-2

No java8, o fluxo do IntSteam pode ser encaixotado no fluxo de inteiros.

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

Eu acho que o desempenho é importante com base no tamanho da matriz.

Soumyakanta Mohapatra
fonte