2015-03-19 11 views
28

Mi chiedevo un codice come questo:C'è un modo per evitare i loop quando si aggiunge a un elenco?

List<String> list = new ArrayList<String>(); 
for(CustomObject co : objects) { 
    list.add(co.getActualText()); 
} 

Può essere scritto in modo diverso? Voglio dire, naturalmente, ad un certo punto ci sarà un ciclo, ma mi chiedo se c'è un utilizzo dell'API sto ignorando

+1

si chiama Clojure; D Puoi leggere su (conj) qui: http://clojuredocs.org/clojure.core/conj – sova

+2

si chiama anche Scala, Haskell e ... (inserisci il nome di qualsiasi potente linguaggio di programmazione) –

risposta

26

Se si utilizza Java 8, è possibile usufruire delle API Stream:

List<String> list = objects.stream() 
          .map(CustomObject::getActualText) 
          .collect(Collectors.toList()); 
+15

TBH IMHO questo è in realtà illeggibile per un compito semplice. Non si tratta della tua risposta, ma dell'approccio Java stesso – Jim

+9

Non proprio d'accordo con Jim, si tratta anche di abituarsi a un nuovo concetto. Anche se in Scala sarebbe più carino con una semplice val list = objects.map (_. GetActualText) –

+7

Le persone .NET hanno LINQ da molti anni ed è chiaro che è un approccio molto migliore rispetto ai loop di costruzione delle liste in quasi tutti i casi. Penso che le persone di Java debbano solo spostarsi per avere un cambiamento di mentalità qui. Questo codice non è illeggibile da solo. È illeggibile per gli inesperti. Questo è uno stato temporaneo. – usr

20

Se si dispone di Java 8, che dire:

objects.forEach(item -> list.add(item.getActualText())); 

Internamente ancora un ciclo però.

MODIFICA un po 'Off-Topic: IMO Questa è la soluzione più leggibile e migliore. Perché non usare solo un foreach che potresti chiedere. La risposta: perché in questo modo la collezione sceglie il modo migliore per scorrere gli articoli. Ad esempio, ArrayList non usa un iteratore, perché sa meglio di te:

@Override 
public void forEach(Consumer<? super E> action) { 
    Objects.requireNonNull(action); 
    final int expectedModCount = modCount; 
    @SuppressWarnings("unchecked") 
    final E[] elementData = (E[]) this.elementData; 
    final int size = this.size; 
    for (int i=0; modCount == expectedModCount && i < size; i++) { 
     action.accept(elementData[i]); 
    } 
    if (modCount != expectedModCount) { 
     throw new ConcurrentModificationException(); 
    } 
} 
+0

Voglio aggiungere che da allora sono cresciuto nell'approccio "funzionale" mostrato da Konstantin Yovkov. Lo stile è molto pulito, anche se sembra orribile e potrebbe funzionare male in Java. – Felk

3

Usando flussi sarebbe più idiomatica in Java 8, ma se vi piace che sia più vicino a quello basato sulla ciclo convenzionale è possibile utilizzare forEach:

objects.forEach(co -> list.add(co.getActualText())); 
6

Anche se chiaramente un po 'di un suggerimento ridicolo: si poteva evitare loop con l'aggiunta di loro in modo ricorsivo.

void add(List<? super String> receiver, CustomObject[] objects) { 
    addRec(receiver, toAdd, 0, objects.length()); 
} 

void addRec(List<? super String> receiver, CustomObject[] objects, int start, int end) { 
    if (start + 1 == end) { 
    receiver.add(objects[start].getActualText()); 
    } else if (start != end) { 
    int mid = (start + end)/2; 
    addRec(receiver, objects, start, mid); 
    addRec(receiver, objects, mid, end); 
    } 
} 
+0

Poiché le operazioni di O (n) richiedono realmente un flusso di controllo che varia dinamicamente (ad esempio un ciclo), questa sarebbe stata la mia risposta. È un po 'trollish in un linguaggio imperativo, ma in un puro linguaggio funzionale, è tutto ciò che hai. +1. – imallett

9

Naturalmente, Apache Commons e Guava forniscono anche i modi per evitare loop senza utilizzare Java 8.

Commons CollectionUtils.collect:

CollectionUtils.collect(objects, Transformer.invokerTransformer("getActualText"), list); 

Guava Lists.transform:

List<String> list = Lists.transform(objects, 
    new Function<CustomObject, String>() { 
     public String apply(CustomObject co) { 
      return co.getActualText(); 
     } 
    } 
); 
+2

In Java 8, l'esempio di Guava può essere scritto in modo molto più conciso sostituendo la classe anonima con un lambda: 'Lists.transform (objects, (co) -> co.getActualText())' – meriton

4

Se usi Eclipse Collections (precedentemente GS Collections) è possibile scrivere quanto segue in Java 8:

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(CustomObject::getActualText); 

con Java 5 - 7 è possibile utilizzare una classe interna anonima che rappresenta il tipo SAM Function con il metodo Collect.

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(new Function<CustomObject, String>() { 
    public String valueOf(CustomObject object){ 
     return object.getActualText(); 
    } 
}); 

Nota: Sono un committer per Eclipse Collezioni

0

Per ottenere davvero un buon rendimento durante la copia di una serie di dati tra due tipi di elenchi che sono sconosciuti gli uni agli altri, ci deve essere un meccanismo con il quale un tipo "fidato" può chiedere a ciascuno di esporre gli array di supporto associati a un intervallo di elementi e quindi utilizzare un'operazione di copia di massa per spostare i dati da uno all'altro. Sarebbe possibile scrivere tale classe interamente in Java, avendo un metodo GetArraySource passare al costruttore di una classe affidabile ArraySource un oggetto che potrebbe utilizzare per richiedere l'array di supporto associato a un particolare elemento (il ritorno includerebbe l'array di supporto e la gamma di elementi inclusi al suo interno).Il codice che desidera la copia chiamerebbe GetArraySource e passava allo ArraySource in tal modo restituito al metodo CopyFromArraySource della lista di destinazione che poteva quindi chiedere allo ArraySource di copiare uno o più intervalli di elementi nei propri array di supporto.

Se ArraySource era una classe fornita con Java e Oracle documentato esattamente quello che avrebbe fatto con gli array che ha ricevuto, allora sarebbe possibile per i tipi come ArrayList e String per esporre il loro contenuto come ArraySource, o accettare i dati esterni da un ArraySource, senza esporre in modo errato la propria matrice a qualsiasi codice che potrebbe abusarne.

Sfortunatamente, a meno che Oracle incorpori una cosa del genere nella distribuzione Java, il supporto sarà probabilmente troppo scarso per essere utile. Non è positivo che l'elenco delle fonti supporti una di queste classi, il supporto di destinazione un'altra e il codice che richiede l'operazione di copia un terzo. Tutte e tre le classi devono supportare la stessa particolare classe helper-segmento-copia-copia.

+0

Penso che tu abbia qualcosa come C memcopy in mente dei dati reali. Destra? – Jim

+0

@Jim: Mi aspetterei che le operazioni di copia bulk in 'Array 'sarebbero probabilmente implementate in modo nativo usando qualcosa di approssimativamente equivalente a' memcpy' dopo aver determinato che nessun oggetto poteva esistere nell'array di origine che il tipo di array di destinazione non poteva tenere premuto [un tentativo di es copiare in massa elementi da un 'Cat []' a un 'Animale [] 'potrebbe semplicemente copiare i riferimenti direttamente, ma andando nell'altro modo sarebbe necessario convalidare il tipo di ogni oggetto referenziato prima di copiare il riferimento]. – supercat