2009-04-20 12 views
7

Supponiamo che io ho un metodo che accetta un array ed elabora ogni elemento al suo interno usando del costruito in for-each loop, come questo Java:C'è un parametro che posso usare in Java che funzioni con tutti i cicli for-each?

public static void myFun(SomeClass[] arr) { 
    for (SomeClass sc : arr) { 
     // Stuff is processed here 
    } 
} 

Questo funziona bene, ma ora voglio essere in grado di passare il lo stesso metodo a List<SomeClass> invece. Sono destinato a utilizzare Collection.toArray(T []) o esiste un parametro che posso utilizzare per myFun() che accetta qualsiasi tipo che può essere utilizzato in un costrutto for-each?

Per chiarire: voglio una firma del metodo che accetti qualsiasi oggetto iterabile, sia esso una matrice primitiva o una raccolta. Posso facilmente scrivere due metodi, uno avvolge l'altro, ma sono curioso di sapere se c'è un modo migliore.

+0

Purtroppo la risposta è no, poiché gli array non implementano Iterable. –

risposta

9

Io suggerirei di usare Iterable, Collection o List come il tipo di parametro.

IMO, le raccolte devono essere preferite per fare riferimento agli array. Se ti capita di avere un array Arrays.asList, la conversione va bene. Arrays.asList consente di ottenere e imposta l'array, ma ovviamente non modifiche "strutturali" che potrebbero modificare la lunghezza dell'array.

myFun(Arrays.asList(arr)); 

Potrebbe essere necessario utilizzare caratteri jolly in casi estremi/generali.

public static void myFun(Iterable<? extends SomeClass> somethings) { 
    for (SomeClass something : somethings) { 
     // something is processed here 
    } 
} 

È interessante notare che Collections.toArray e Arrays.asList lavoro leggermente diverso. asList mantiene la matrice originale per il retro della raccolta, pertanto le modifiche alla raccolta si rifletteranno nell'array. Collections.toArray crea una copia (superficiale) dei dati della raccolta. Fare una copia è spesso ciò che si vorrebbe comunque se si restituisce un array. In modo asimmetrico, se si passa come argomento generalmente non si copia (a meno che non si memorizzi come campo).

+0

Accetto; per casi come questo uso Iterable poiché impone il minimo onere agli implementatori. E anche se non funziona direttamente con Object [], è banale colmare questo problema con Arrays.asList(). – erickson

+0

Sì. OTOH, l'implementazione potrebbe concepire la dimensione della raccolta prima di iterare e la maggior parte del codice utilizza ancora Collection. –

+0

Dato che non sembra essere un modo per evitare asList() o toArray(), sono più d'accordo con questa logica; ha senso usare Iterable invece di Object [] se mi aspetto l'input da entrambi. –

5

Utilizzare Iterable. Ecco a cosa serve

Come hai detto, Iterable non gestirà gli array.

Non si desidera utilizzare più metodi che si completano a vicenda. Questo esclude Arrays.asList e Collection.toArray.

Quindi la risposta alla tua domanda è no, non c'è un modo. Ma se tu puoi usare Lists, perché dovresti usare mai gli array?

Vorrei ancora andare con Iterable qui. Mi piace meglio di Collection perché in passato ho avuto classi che implementavano Iterable ma non erano raccolte; questo ha reso facile per loro recuperare pigramente i valori secondo necessità, e potrei usare il ciclo foreach su di essi.

+2

Se si utilizza Iterable come parametro, si ottengono errori di compilazione quando si tenta di passare agli array primitivi. –

+0

Ooh, non ci avevo pensato. Fammi pensare un po '... –

+0

Is Object [] Iterable ?? –

-3
public static void myFun(Collection<MyClass> collection) { 
    for (MyClass mc : collection) { 
     // Stuff is processed here 
    } 
} 
+0

MyCalss. –

+0

Questo non funzionerà con il passaggio in un array –

3

non si può, Array Java non implementa Iterable:

public static int sum(Iterable<Integer> elements) { 
    int s = 0; 

    for (int i : elements) { 
     s += i; 
    } 

    return s; 
} 

public static void main(String[] args) { 
    L1: System.out.println(sum(1,2,3)); 
    L2: System.out.println(sum(Arrays.asList(1,2,3))); 
    L3: System.out.println(sum(new int[] { 1,2,3 })); 
} 

questo si traduce in due errori di compilazione in (L1 e L3); quindi è necessario progettare il metodo ad accettare un Iterable (Collezioni) e/o un array, almeno un metodo deve eseguire alcune di conversione (da/per array)

SOLUZIONE:

voi potrebbero provare con un adattatore:

public class ArrayIterator<T> implements Iterator<T> { 

    private final T[] array; 
    private int i; 

    public ArrayIterator(T[] anArray) { 
     array = anArray; 
     i = 0; 
    } 

    public boolean hasNext() { 
     return i < array.length; 
    } 

    public T next() { 
     return array[i++]; 
    } 

    public void remove() { 
     throw new UnsupportedOperationException(); 
    } 
} 

private static int sum(final Integer ... elements) { 
    return sum(new Iterable<Integer>() { 

     public Iterator<Integer> iterator() { 
      return new ArrayIterator<Integer>(elements); 
     } 
    }); 
} 

si dovrebbe prestare attenzione solo quando si tratta di matrici primitive; quando si utilizza solo oggetto di riferimento (il tuo caso) ArrayIterator + classe anonima sono fresche

Speranza che aiuta

0

C'è una caratteristica sanno poco di Java Generics in Java 1.5 + dove è possibile utilizzare <? extends Subtype> nelle chiamate di metodo e costruttori. È possibile utilizzare <? extends Object> e quindi tutto ciò che riguarda quelli avrebbe accesso solo ai metodi su Oggetto. Che cosa si potrebbe vuole veramente è qualcosa di più simile a questo:

List<? extends MyCrustaceans> seaLife = new ArrayList<? extends MyCrustaceans>(); 
MyShrimp s = new MyShrimp("bubba"); 
seaLife.add(s); 
DoStuff(seaLife); 

... 

public static void DoStuff(List<? extends MyCrustaceans> seaLife) 
{ 
    for (MyCrustaceans c : seaLife) { 
     System.out.println(c); 
    } 
} 

Quindi, se si dispone di una classe di base (come MyCrustaceans), è possibile utilizzare qualsiasi metodo di quella classe base in DoStuff (o della classe Object, se il vostro parametro è <? extends Object>). C'è una funzionalità simile a <? super MyType>, in cui accetta un parametro che è un supertipo del tipo specificato, anziché un sottotipo. Ci sono alcune restrizioni su cosa puoi usare "extends" e "super" per questo modo. Ecco uno good place to find more info.

1

Risposta breve: no, non esiste una singola firma del metodo che digita in modo sicuro sia un Iterable che un array. Ovviamente potresti semplicemente accettare Object, ma anche quello sarebbe un trucco.

Risposta lunga-ish: Dal momento che la maggiore for -loop viene efficacemente definito due volte (una volta per gli array e una volta per Iterable), è necessario fornire due metodi di overload così:

public static void myFun(SomeClass[] array) { 
    for (SomeClass sc : array) { 
     doTheProcessing(sc); 
    } 
} 

public static void myFun(Iterable<? extends SomeClass> iterable) { 
    for (SomeClass sc : iterable) { 
     doTheProcessing(sc); 
    } 
} 

Anche se la la fonte dei due metodi sembra esattamente la stessa, è necessario definirla due volte (a meno che, naturalmente, non si avvolga l'array nel proprio Iterable come @dfa delineato).

+0

"Per chiarire: voglio una firma del metodo che accetti qualsiasi oggetto iterabile, sia esso una matrice primitiva o una collezione.Posso molto facilmente scrivere due metodi, con uno che avvolge l'altro, ma sono solo curioso di sapere se c'è un modo migliore. " –

+0

Ok, allora non ho letto abbastanza bene la domanda. Spero che questo sia migliore. –

+1

Sì, non c'è davvero altra risposta. Tutte le "risposte" sono solo soluzioni alternative o consigli. –

Problemi correlati