Konvertieren Sie Java Array in Iterable

150

Ich habe ein Array von Grundelementen, zum Beispiel für int, int [] foo. Es könnte klein sein oder nicht.

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

Was ist der beste Weg, um daraus ein zu erstellen Iterable<Integer>?

Iterable<Integer> fooBar = convert(foo);

Anmerkungen:

Bitte antworten Sie nicht mit Schleifen (es sei denn, Sie können eine gute Erklärung geben, wie der Compiler etwas Kluges dagegen unternimmt?)

Beachten Sie auch das

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

Wird nicht einmal kompilieren

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

Aktivieren Sie auch Warum kann ein Array nicht Iterable zugewiesen werden? vor der Antwort.

Wenn Sie eine Bibliothek (z. B. Guave) verwenden, erklären Sie bitte, warum dies die beste ist. (Weil es von Google keine vollständige Antwort ist: P)

Zuletzt, da es eine Hausaufgabe zu geben scheint, vermeiden Sie es, Hausaufgabencode zu veröffentlichen.

ntg
quelle
mögliches Duplikat von Iterator für Array
NPE
Fügen Sie sie einer LinkedList hinzu und geben Sie einfach den Iterator dieses Sets zurück.

Antworten:

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

Sie müssen jedoch ein IntegerArray (kein intArray) verwenden, damit dies funktioniert.

Für Grundelemente können Sie Guave verwenden:

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

Für Java8: (aus Jin Kwons Antwort)

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
quelle
10
Zwei Anmerkungen: 1) er hat int, nicht Integer2) Listist schon Iterableso ist die dritte Zeile sinnlos.
Maksimov
1
er braucht Iterable, warum es eine dritte Zeile gibt.
Fmucar
5
2. und 3. Zeile sind Optionen, die ich sagen würde :)
fmucar
1
Dies ist nicht Teil einer Hausaufgabe, ich habe nur versucht, das Duplizieren von Code für eine Debugging-Funktion zu vermeiden, die den Inhalt eines Arrays oder einer Liste verarbeitet ... Während ich mich umsah, fand ich tatsächlich Arrays.asList (..);, aber zumindest Eclipse scheint zu glauben, dass es nicht das tut, was ich will (z. B. das Ergebnis von Arrays.asList (foo) als Liste <int []>, nicht als Liste <Integer> ...). Ich fand das interessant genug für a Frage ... (-Brechender Kommentar in Teilen Ursache von Grenzen-)
ntg
1
Im Allgemeinen könnte man sich viele Möglichkeiten vorstellen, dies zu tun, aber ich habe mich gefragt, was das BESTE ist (zum Beispiel wäre eine Schleife imh langsamer als ... {Nun, das Problem ist, dass mir nichts einfällt!: )}) Unter stackoverflow.com/questions/1160081/… finden Sie auch eine Diskussion darüber, warum. Meine Frage ist jedoch nicht warum, sondern wie und welcher Containertyp am besten geeignet ist (warum ArrayList? Tatsächlich könnte ich mir eine AbstractList vorstellen Wrapper mit Generika .., hängt wahrscheinlich von der Größe ab ...)
ntg
44

nur meine 2 Cent:

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.");
            }
        };
    }
};
Jörg Ruethschilling
quelle
9
remove () ist in Java 8 nicht erforderlich, da es sich um eine Standardmethode handelt, die UnsupportedOperationException auslöst. Nur wenn Sie eine bessere Erklärungsnachricht liefern möchten.
Alex
+1 Ich mache etwas Ähnliches, um ein Iterator<Character>aus einem zu erstellen String. Das Implementieren eigener IteratorWerte scheint der einzige Weg zu sein, um zu vermeiden, dass unnötig alle Werte durchlaufen werden, um vom Objekttyp in den primitiven Typ zu konvertieren ( Ints.asList()z. B. über Guavas ), nur um einen Iteratorvon dem Listerstellten zu erhalten.
spaaarky21
2
Du hast recht, Alex. Java 8 wurden Standardmethoden hinzugefügt. 2013 habe ich diesen alten Code hier hinzugefügt.
Jörg Ruethschilling
29

Mit Java 8 können Sie dies tun.

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

Guava stellt den gewünschten Adapter als Int.asList () bereit . Für jeden primitiven Typ in der zugehörigen Klasse gibt es ein Äquivalent, z. B. Booleansfür booleanusw.

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

Die oben genannten Vorschläge Arrays.asListfunktionieren nicht, auch wenn sie kompiliert werden, weil Sie Iterator<int[]>eher eine als erhalten Iterator<Integer>. Anstatt eine von Ihrem Array unterstützte Liste zu erstellen, haben Sie eine 1-Element-Liste von Arrays erstellt, die Ihr Array enthält.

BeeOnRope
quelle
Nur ein Hinweis: Der Link funktioniert nicht mehr. Github-Link: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle
Vielen Dank an @Passi, behoben (es scheint keine von Google unterstützte Möglichkeit mehr zu geben, auf javadoc zu verlinken, daher habe ich auf die von Ihnen angegebene Quelle verlinkt).
BeeOnRope
8

Ich hatte das gleiche Problem und löste es so:

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

Der Iterator selbst ist faul, UnmodifiableIteratoraber genau das habe ich gebraucht.

mindas
quelle
7

In Java 8 oder höher Iterablewird eine funktionale Schnittstelle zurückgegeben Iterator. Also kannst du das machen.

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

->

1
2
3
saka1029
quelle
3

Zunächst kann ich nur zustimmen, dass dies Arrays.asList(T...)eindeutig die beste Lösung für Wrapper-Typen oder Arrays mit nicht primitiven Datentypen ist. Diese Methode ruft einen Konstruktor einer einfachen privaten statischen AbstractListImplementierung in der ArraysKlasse auf, der im Grunde die angegebene Array-Referenz als Feld speichert und eine Liste simuliert, indem die erforderlichen Methoden überschrieben werden.

Wenn Sie für Ihr Array zwischen einem primitiven Typ oder einem Wrapper-Typ wählen können, würde ich den Wrapper-Typ für solche Situationen verwenden, aber natürlich ist er nicht immer nützlich oder erforderlich. Es gibt nur zwei Möglichkeiten:

1) Sie können eine Klasse mit einer statischen Methode für jedes primitive Datentyp-Array erstellen ( boolean, byte, short, int, long, char, float, doubleRückgabe eines Iterable<WrapperType >. Diese Methoden würden anonyme Klassen von Iterator(außerdem) verwendenIterable), die die Referenz des Arguments der umfassenden Methode (zum Beispiel an int[]) als Feld enthalten dürfen, um die Methoden zu implementieren.

-> Dieser Ansatz ist performant und spart Ihnen Speicherplatz (mit Ausnahme des Speichers der neu erstellten Methoden, obwohl die Verwendung Arrays.asList()auf die gleiche Weise Speicherplatz beanspruchen würde).

2) Da Arrays keine Methoden haben (um auf der Seite gelesen zu werden) Sie haben keine IteratorInstanz bereitgestellt. Wenn Sie wirklich zu faul sind, um neue Klassen zu schreiben, müssen Sie eine Instanz einer bereits vorhandenen Klasse verwenden, die implementiert wird, Iterableda es keinen anderen Weg gibt als das Instanziieren Iterableoder einen Subtyp.
Der EINZIGE Weg, um eine vorhandene Implementierung eines Sammlungsderivats zu erstellenIterableVerwenden Sie eine Schleife (außer Sie verwenden anonyme Klassen wie oben beschrieben) oder instanziieren Sie eine Iterableimplementierende Klasse, deren Konstruktor ein primitives Typarray zulässt (da Object[]Arrays mit primitiven Tybelementen nicht zulässig sind), aber meines Wissens die Java-API gibt es keine solche Klasse.

Der Grund für die Schleife kann leicht erklärt werden:
Für jede Sammlung benötigen Sie Objekte, und primitive Datentypen sind keine Objekte. Objekte sind viel größer als primitive Typen, sodass zusätzliche Daten erforderlich sind, die für jedes Element des primitiven Typarrays generiert werden müssen. Das heißt, wenn zwei Dreierarten (Verwenden Arrays.asList(T...)oder Verwenden einer vorhandenen Sammlung) ein Aggregat von Objekten erfordern, müssen Sie für jeden primitiven Wert von Ihren erstellenint[]Array das Wrapper-Objekt. Der dritte Weg würde das Array so wie es ist verwenden und es in einer anonymen Klasse verwenden, da ich denke, dass es aufgrund der schnellen Leistung vorzuziehen ist.

Es gibt auch eine dritte Strategie, bei der ein Objectas-Argument für die Methode verwendet wird, bei der Sie das Array verwenden möchten, oder Iterablebei der Typprüfungen erforderlich sind, um herauszufinden, welchen Typ das Argument hat. Ich würde es jedoch überhaupt nicht empfehlen, wie Sie es normalerweise benötigen Beachten Sie, dass das Objekt nicht immer den erforderlichen Typ hat und dass Sie in bestimmten Fällen separaten Code benötigen.

Zusammenfassend ist es die Schuld von Javas problematischem Generic Type-System, das es nicht erlaubt, primitive Typen als generischen Typ zu verwenden, der durch einfache Verwendung viel Code sparen würdeArrays.asList(T...). Sie müssen also für jedes primitive Typarray eine solche Methode programmieren (die im Grunde genommen keinen Unterschied zum von einem C ++ - Programm verwendeten Speicher macht, der für jedes verwendete Typargument eine separate Methode erstellen würde.

CodingVirus01
quelle
3

Sie können IterableOfvon Cactoos verwenden :

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

Dann können Sie daraus eine Liste machen, indem Sie ListOf:

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

Oder einfach das:

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

Obwohl eine ähnliche Antwort bereits veröffentlicht wurde, war der Grund für die Verwendung des neuen PrimitiveIterator.OfInt meines Erachtens nicht klar. Eine gute Lösung ist die Verwendung von Java 8 PrimitiveIterator, da es auf primitive int-Typen spezialisiert ist (und die zusätzliche Strafe für das Ein- und Auspacken vermeidet):

    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
quelle
-2

In Java8 kann der IntSteam-Stream in einen Stream mit Ganzzahlen umgewandelt werden.

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

Ich denke, die Leistung hängt von der Größe des Arrays ab.

Soumyakanta Mohapatra
quelle