Konwertuj tablicę Java na iterowalną

150

Mam tablicę prymitywów, na przykład dla int, int [] foo. Może to być mały rozmiar lub nie.

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

Jaki jest najlepszy sposób na stworzenie Iterable<Integer>z tego pliku ?

Iterable<Integer> fooBar = convert(foo);

Uwagi:

Proszę nie odpowiadać za pomocą pętli (chyba że możesz dobrze wyjaśnić, w jaki sposób kompilator zrobił z nimi coś inteligentnego?)

Zwróć też na to uwagę

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

Nawet się nie skompiluje

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

Sprawdź także, dlaczego tablicy nie można przypisać do iterowalnej? zanim odpowiesz.

Ponadto, jeśli używasz jakiejś biblioteki (np. Guava), wyjaśnij, dlaczego jest najlepsza. (Bo to od Google nie jest pełną odpowiedzią: P)

Wreszcie, ponieważ wydaje się, że jest zadanie domowe na ten temat, unikaj publikowania kodu pracy domowej.

ntg
źródło
możliwy duplikat Iteratora dla tablicy
NPE
Dodaj je do LinkedList, a następnie po prostu zwróć iterator tego zestawu.

Odpowiedzi:

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

Chociaż musisz użyć Integertablicy (nie inttablicy), aby to zadziałało.

W przypadku prymitywów możesz użyć guawy:

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

Dla Java8: (z odpowiedzi 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
źródło
10
Dwie uwagi: 1) ma int, a nie Integer2) Listjuż jest, Iterablewięc trzecia linijka jest bezcelowa.
maksimov
1
potrzebuje powtarzalności, dlaczego jest trzecia linia.
fmucar
5
Druga i trzecia linia to opcje, które powiedziałbym :)
fmucar
1
To nie jest część pracy domowej, po prostu starałem się uniknąć powielania kodu funkcji debugowania przetwarzającej zawartość tablicy lub listy ... Rozglądając się, rzeczywiście znalazłem Arrays.asList (..);, ale przynajmniej Eclipse wydaje się myśleć, że nie zrobi tego, co chcę (np. Wnioskuje wynik Arrays.asList (foo) jako List <int []>, a nie List <Integer> ...). pytanie ... (-Breaking komentarz w części przyczyną limits-)
NTG
1
Ogólnie można by wymyślić wiele sposobów na zrobienie tego, ale zastanawiałem się, który jest NAJLEPSZY (na przykład pętla byłaby wolniejsza niż ... {no cóż, problem w tym, że nie mogę nic wymyślić !: )}) Sprawdź także stackoverflow.com/questions/1160081/…, aby zapoznać się z dyskusją na temat tego, dlaczego, ale moje pytanie nie brzmi: dlaczego, ale jak i jaki typ kontenera byłby najlepszy (dlaczego ArrayList? Właściwie mógłbym sobie wyobrazić jakąś AbstractList wrapper przy użyciu Generics .., Prawdopodobnie zależy od rozmiaru ...)
ntg
44

tylko moje 2 centy:

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
źródło
9
remove () nie jest konieczna w java 8, ponieważ jest to domyślna metoda, która zgłasza wyjątek UnsupportedOperationException. Tylko jeśli chcesz podać lepsze wyjaśnienie.
Alex
+1 Robię coś podobnego, aby utworzyć Iterator<Character>z String. Wdrożenie własnego Iteratorwydaje się jedynym sposobem na uniknięcie niepotrzebnego iterowania przez wszystkie wartości w celu konwersji z typu obiektu do typu pierwotnego ( Ints.asList()na przykład przez Guava ), tylko po to, aby móc uzyskać wynik Iteratorz tego, Listktóry został utworzony.
spaaarky21
2
Masz rację Alex. Domyślne metody zostały dodane do języka Java 8. W 2013 roku dodałem tutaj ten starożytny fragment kodu.
Joerg Ruethschilling,
29

Dzięki Java 8 możesz to zrobić.

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
źródło
20

Guava dostarcza żądany adapter jako Int.asList () . Jest odpowiednikiem dla każdego rodzaju prymitywnego w dodatkowej klasy, na przykład, Booleansdla booleanitp

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

Powyższe sugestie dotyczące użycia Arrays.asListnie będą działać, nawet jeśli zostaną skompilowane, ponieważ otrzymasz Iterator<int[]>zamiast Iterator<Integer>. Dzieje się tak, że zamiast tworzyć listę wspieraną przez twoją tablicę, utworzyłeś 1-elementową listę tablic, zawierającą twoją tablicę.

BeeOnRope
źródło
uwaga: link już nie działa. Link do Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle,
Dzięki @Passi, naprawione (wydaje się, że nie mogę znaleźć obsługiwanego przez Google sposobu na połączenie z javadoc, więc połączyłem się z podanym źródłem).
BeeOnRope
8

Miałem ten sam problem i rozwiązałem go w ten sposób:

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

Sam iterator jest leniwy, UnmodifiableIteratorale właśnie tego potrzebowałem.

mindas
źródło
7

W Javie 8 lub nowszym Iterablezwraca funkcjonalny interfejs Iterator. Więc możesz to zrobić.

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

->

1
2
3
saka1029
źródło
3

Przede wszystkim mogę się tylko zgodzić, że Arrays.asList(T...)jest to zdecydowanie najlepsze rozwiązanie dla typów Wrapper lub tablic z innymi niż pierwotne typy danych. Ta metoda wywołuje konstruktor prostej prywatnej AbstractListimplementacji statycznej w Arraysklasie, która w zasadzie zapisuje podane odwołanie do tablicy jako pole i symuluje listę, zastępując potrzebne metody.

Jeśli możesz wybrać typ pierwotny lub typ Wrapper dla swojej tablicy, użyłbym typu Wrapper w takich sytuacjach, ale oczywiście nie zawsze jest on przydatny lub wymagany. Byłyby tylko dwie możliwości, które możesz zrobić:

1) Możesz utworzyć klasę ze statyczną metodą dla każdej pierwotnej tablicy typu danych ( boolean, byte, short, int, long, char, float, doublezwracając Iterable<WrapperType >. Te metody używałyby anonimowych klas Iterator(pozaIterable), które mogą zawierać odwołanie do argumentu metody zawierającej (na przykład int[]) jako pole w celu zaimplementowania metod.

-> To podejście jest wydajne i oszczędza pamięć (z wyjątkiem pamięci nowo utworzonych metod, chociaż użycie Arrays.asList()zajmie pamięć w ten sam sposób)

2) Ponieważ tablice nie mają metod (jak do odczytu z boku z linkiem), nie mogą też zapewnić Iteratorinstancji. Jeśli naprawdę jesteś zbyt leniwy, aby pisać nowe klasy, musisz użyć instancji już istniejącej klasy, która implementuje, Iterableponieważ nie ma innego wyjścia niż utworzenie instancji Iterablelub podtyp.
JEDYNY sposób na utworzenie istniejącej implementacji pochodnej kolekcjiIterablepolega na użyciu pętli (z wyjątkiem używania klas anonimowych, jak opisano powyżej) lub utworzeniu instancji Iterableklasy implementującej, której konstruktor zezwala na tablicę typu pierwotnego (ponieważ Object[]nie zezwala na tablice z elementami typu pierwotnego), ale o ile wiem, API Java nie ma takiej klasy.

Powód pętli można łatwo wyjaśnić:
dla każdej kolekcji potrzebujesz Obiekty, a pierwotne typy danych nie są obiektami. Obiekty są znacznie większe niż typy pierwotne, dlatego wymagają dodatkowych danych, które muszą zostać wygenerowane dla każdego elementu tablicy typów pierwotnych. Oznacza to, że jeśli dwa z trzech sposobów (używanie Arrays.asList(T...)lub używanie istniejącej kolekcji) wymagają agregacji obiektów, musisz utworzyć dla każdej pierwotnej wartości swojejint[]w szyku obiekt opakowania. Trzeci sposób użyłby tablicy w takiej postaci, w jakiej jest, i użyłby jej w klasie anonimowej, ponieważ uważam, że jest to preferowane ze względu na dużą wydajność.

Istnieje również trzecia strategia wykorzystująca Objectargument jako argument dla metody, w której chcesz użyć tablicy lub Iterablektóra wymagałaby sprawdzenia typu, aby dowiedzieć się, jaki typ ma argument, jednak nie polecałbym tego w ogóle, ponieważ zwykle musisz weź pod uwagę, że Object nie zawsze ma wymagany typ i że w niektórych przypadkach potrzebujesz osobnego kodu.

Podsumowując, jest to wina problematycznego systemu typów ogólnych w Javie, który nie pozwala na użycie typów pierwotnych jako typów ogólnych, co pozwoliłoby zaoszczędzić dużo kodu, używając prostegoArrays.asList(T...). Więc musisz zaprogramować taką metodę dla każdej tablicy typów pierwotnych (która w zasadzie nie robi różnicy w pamięci używanej przez program w C ++, który utworzyłby dla każdego użytego argumentu typu oddzielną metodę.

CodingVirus01
źródło
3

Możesz użyć IterableOfod Cactoos :

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

Następnie możesz przekształcić go w listę za pomocą ListOf:

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

Lub po prostu to:

List<String> names = new ListOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);
yegor256
źródło
1

Chociaż podobna odpowiedź została już opublikowana, myślę, że powód użycia nowego PrimitiveIterator.OfInt nie był jasny. Dobrym rozwiązaniem jest użycie Java 8 PrimitiveIterator, ponieważ jest ona wyspecjalizowana w prymitywnych typach int (i pozwala uniknąć dodatkowej kary za 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
źródło
-2

W java8 strumień IntSteam może być opakowany w strumień liczb całkowitych.

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

Myślę, że wydajność ma znaczenie w zależności od rozmiaru tablicy.

Soumyakanta Mohapatra
źródło