2017-01-27 2 views
46

Ich habe eine Anwendung, wo ich primitive Arrays und Listen für eine Klasse namens Artikel verwenden. Diese werden aus Legacy-Gründen austauschbar verwendet (ich wünschte auch, das wäre nur ein Typ, aber so ist es).Wie reformiere ich diese Schleife?

Jetzt habe ich eine neue Methode wie folgt hinzufügen, die über eine für-jede Schleife funktioniert:

public void something(Item... items) { 
    for (Item i : items) { 
     doStuff(); 
    } 
} 

public void something(List<Item> items) { 
    for (Item i : items) { 
     doStuff(); 
    } 
} 

Mit anderen Worten, genau die gleiche Methode zweimal für beide primitive Arrays und Listen. Gibt es eine Möglichkeit, dies in eine einzige Methode umzuwandeln?

+0

ich würde es vorziehen, 'for-each' nicht für' List' zu verwenden, es funktioniert nicht –

+5

@Mohsen_Fatemi Wie funktioniert ein 'for-each' nicht auf' List' Objekten? –

+2

Uhh, warum sollte es nicht funktionieren? Der Code, den ich hier bekommen habe, funktioniert gut, er ist einfach nicht gut umstrukturiert. – Selbi

Antwort

67

Sie kann nicht sollte nicht (*) dies in einer einzigen Methode tun. Item[] und List<Item> sind nicht verwandte Typen.

sollten Sie zu einem der Überlastung des anderen rufen: entweder something(Item... items) Anrufe something(List<Item>) oder something(List<Item>) Anrufe something(Item... items).

Von den beiden Optionen ist es besser, für das Array die Liste Überlastung zu nennen Überlastung:

public void something(Item... items) { 
    something(Arrays.asList(item)); 
} 

Das ist billig, weil es nicht das Array kopiert, sondern wickelt es: die Schaffung der List ist O(1).

Wenn Sie waren die Array-Überlastung aus der Liste Überlastung aufzurufen:

public void something(List<Item> items) { 
    something(items.toArray(new Item[0])); 
} 

Diese teurer sein würde, da der toArray Aufruf erstellen hat und ein Array zu füllen: es ist ein O(n) Betrieb, wo n ist die Größe der Liste. Es hat jedoch den leichten Vorteil, dass something den Inhalt von List nicht ersetzen kann, da alle Aktualisierungen des Arrays nach der Ausführung einfach verworfen werden.


(*) Sie können , aber es wäre wirklich gross sein, und nicht typsicher, wie Sie einen Object Parameter akzeptieren müssten, da es keine anderen gemeinsamen Supertyp List<Item> ist und Item[]; und du würdest immer noch die Schleifen für die zwei Typen wiederholen müssen; und Sie müssten mit der Möglichkeit umgehen, dass ein völlig unzusammenhängender Typ (zur Laufzeit) übergeben wird:

public void something(Object obj) { 
    if (obj instanceof List) { 
    for (Object element : (List<?>) obj) { 
     Item item = (Item) element; // Potential ClassCastException. 
     doStuff(); 
    } 
    } else if (obj instanceof Item[]) { 
    for (Item item : (Item[]) obj) { 
     doStuff(); 
    } 
    } else { 
    throw new IllegalArgumentException(); 
    } 
} 

Was für ein Durcheinander. Danke dem Hersteller für Überladungen.

+0

Ich wäre daran interessiert zu sehen, wie Sie es tun könnten, nur aus Neugier. Bearbeiten: das macht Sinn, Sie müssten eine Bedingung in dort haben, um herauszufinden, welcher Typ übergeben wurde. –

+1

@MichaelCurry lesen Sie diesen Teil: "... als müssten Sie einen' Object' Parameter akzeptieren. .. "- Sie würden grundsätzlich den Typ des Parameters innerhalb der Methode überprüfen und entsprechend handeln. Allerdings kann man damit nichts erreichen, da sowohl die Komplexität des Codes als auch die Fragilität steigen würde (Sie könnten auch andere Dinge an diese Methode übergeben, zB einfache Strings oder Zahlen. – Thomas

+0

@MichaelCurry da sind Sie. –

0

Sie sollten dies erreichen, indem Sie List nach dem Konvertieren in Array übergeben.

Bewahren Sie diese als einzige Methode,

public void something(Item... items) { 
    for (Item i : items) { 
     doStuff(); 
    } 
} 

und wenn Sie ein List<Item> dann passieren, wie dies passieren wollen,

something(listItem.toArray(new Item[listItem.size()]))

+0

Wie in dem anderen Beitrag unten erwähnt, funktioniert dies, bringt aber den Overhead mit sich, das Array zuerst zu konvertieren. Ich hatte gehofft, dass es einen Weg gibt, dies "direkt" zu tun. – Selbi

+5

Dies ist der falsche Weg, um die Konvertierung durchzuführen: Dies erfordert eine Kopie des Arrays zu machen; Wenn Sie jedoch ein Array mit 'Arrays.toList' in eine Liste ändern, wird das Array einfach umkopiert. Also ist es O (n) gegen O (1). –

+1

Beachten Sie auch, dass die aktuelle Weisheit ist, dass 'new Item [0]' tatsächlich effizienter ist als 'new Item [listItem.size()]'. Auf der Suche nach einer Referenz ... –

1

Sie können eine einzelne Methode implementieren, in diesem Fall , die zweite, weil sie eine Liste als Parameter hat.Anstelle der ersten Methode können Sie das Array in einer Liste mit Arrays.asList(items) konvertieren und dann können Sie die erste Methode aufrufen. Am Ende haben Sie nur eine einzige Methode (die eine Liste als Parameter hat).

Auch wenn die Artikel Liste wenige Elemente hat, können Sie die Lambda-Ausdrücke aus Java 8 verwenden:

items.foreach(item -> doStuff(item)); 

Also, werden Sie nicht eine Methode, die nur eine Schleife und der Code enthält, wird leichter sein, lesen.

+0

oder sogar 'items.foreach (ClassName :: doStuff)' – eis

15

Wenn Sie Java 8 verwenden, können Sie auch einfach forEach oder map auf Ihrem Stream anrufen, und Sie sind fertig, z.

yourStream.forEach(doStuff()); 

wo doStuff() ist die consumer mit dem String zu tun oder yourStream.forEach(s -> doStuff()) verwenden, wenn Sie möchten die Zeichenfolge nicht handhaben und nur do stuff.

Sie können einen Strom erhalten wie folgt:

Stream.of(yourArray) // or Arrays.stream(yourArray) 
     .forEach(doStuff()); 

und für die Liste:

list.stream() 
    .forEach(doStuff()); 

Der Hauptvorteil der Ströme verwendet, ist wahrscheinlich die Lesbarkeit. Es kann in Bezug auf die Leistung verlieren und es kann auch verlieren, wenn Sie nicht Stream.of/Arrays.stream oder Collection.stream() aufrufen möchten, nur um den Stream zu erhalten.

Wenn Sie wirklich behalten wollen die something(...) Methode (sowohl mit der Lage zu befassen: die varargs und die Liste) Sie noch eine überladene Methode oder verwenden Andy Turner's proposal mit dem Object -parameter-Verfahren erfordern.

+1

Aber Sie müssten immer noch zwei überladene Methoden haben, um einen Stream zu erhalten ... richtig? – Fildor

+0

Ja, das stimmt. es ist wahrscheinlich auch nur eine Frage von Präferenz, Lesbarkeit und Leistung. Ich finde Streams die meiste Zeit lesbarer. – Roland

Verwandte Themen