2012-02-06 12 views
8

ho lista List<Custom> dove Custom è comenumero Conte di oggetti con proprietà

class Custom{ 
    public int id; 
    public String name; 
} 

Come ottenere il numero di articoli che hanno nome "Tom"? C'è un modo più semplice di un ciclo for?

+0

Non c'è 'Linq' per Java, sebbene ci sia questo ... http://stackoverflow.com/questions/346721/linq-for-java-tool – xandercoded

risposta

2

Non esiste una soluzione più semplice con le collezioni standard. Devi scorrere l'elenco e contare le occorrenze del nome.

1

O si tiene traccia mentre si aggiungono o rimuovono elementi dall'elenco. Questo potrebbe prendere il posto di un hashmap Nome-> Conteggio. Dove quando aggiungi un oggetto incrementa il conteggio per quel nome e quando lo rimuovi decrementi il ​​conteggio.

Oppure si itera sulla raccolta con un controllo del ciclo per il nome in questione.

A seconda del comportamento dell'applicazione, uno di questi metodi sarà più veloce ma senza ulteriori informazioni è difficile dire quale.

1

Più facile probabilmente no. Ora è possibile memorizzare gli oggetti in una stringa < della mappa, <personalizzata> > invece dove la chiave è il nome. Per ottenere il numero di elementi in cui il nome == "Tom" si può poi semplicemente fare:

List<Custom> l = myMap.get("Tom"); 
int count = 0; 
if (l != null) { 
    count = l.size(); 
} 
1

Si può anche ordinare la lista prima del loop, quindi utilizzare divide et impera per trovare le corrispondenze, poi contare. Dipende davvero dalle tue esigenze, come quanti elementi? Ci sono molti inserimenti dopo una ricerca? ecc

1

Il modo in cui è ora definito richiederà sempre il ciclo sull'elenco.

La creazione di un indice secondario con una mappa di nomi nell'elenco di id è una buona idea.

Un'ulteriore opzione sarebbe assicurarsi che l'elenco sia ordinato per nome, nel qual caso tutti i "Tom" saranno memorizzati l'uno accanto all'altro. Quindi è possibile trovare il pugno "Tom" in tempo O (log (n)) con una ricerca binaria e continuare a contare da lì fino a quando non si raggiunge un "Tom" o una fine dell'elenco. L'operazione di inserimento avrà O (n) complessità in quanto è necessario spostare tutti gli elementi dopo la posizione di inserimento di una posizione, quindi considerare attentamente :-)

4

Personalmente, mi piace usare il lib Apache Commons Collection quando posso. (Ma quello su sourceforge dato che usa i generici) Permette di fare cose molto eleganti come liste di mappe o elenchi di filtri (in modo schema-ish). Si finirebbe per scrivere qualcosa di simile:

int count = CollectionUtils.countMatches(myList, new Predicate<Custom>(){ 
    public boolean evaluate(Custom c){ return "Tom".equals(c.name); } 
} 

L'unico inconveniente è che, poiché Java non hanno funzioni di primo ordine, si deve scrivere piccoli oggetti come tale istanza predicato. Sarebbe più pulito se potessi scrivere funzioni anonime. cioè in scala, sarebbe questo:

val myList = List("a", "a", "b", "c", "a") 
val c = myList.count{ "a".equals(_) } // is 3 
5

Se avete intenzione di filtrare in base al nome in più punti, e in particolare se si sta andando a catena che filtro con gli altri determinati in fase di esecuzione, Google Guava predicati può aiutare:

public static Predicate<Custom> nameIs(final String name) { 
    return new Predicate<Custom>() { 
     @Override public boolean apply(Custom t) { 
      return t.name.equals(name); 
     } 
    }; 
} 

Una volta codificato quel predicato, il filtraggio e il conteggio richiedono solo una riga di codice.

int size = Collections2.filter(customList, nameIs("Tom")).size(); 

Come si può vedere, la costruzione prolissa di un predicato (stile funzionale) non sarà sempre più leggibile, più veloce o risparmiare righe di codice rispetto ai cicli (stile imperativo). In realtà, Guava documentation indica esplicitamente che lo stile imperativo deve essere utilizzato per impostazione predefinita. Ma i predicati sono uno strumento piacevole da avere comunque.

0

Che dire di questo? :

package test; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.Map; 

public class CustomArrayBuilder extends ArrayList<Custom> { 

    Map<String, Integer> namesMap = new HashMap<String, Integer>(); 

    public CustomArrayBuilder(Collection<? extends Custom> c) { 
     super(c); 
     this.prepareAddAll(c); 
    } 

    public int getDifferentNamesAmount() { 
     return this.namesMap.size(); 
    } 

    public int getNameAmount(String name) { 
     Integer integer = this.namesMap.get(name); 
     return (integer != null) ? integer : 0; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public Custom set(int index, Custom element) { 
     Custom custom = super.set(index, element); 
     prepareSet(custom, element); 
     return custom; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public boolean add(Custom e) { 
     this.prepareAdd(e); 
     return super.add(e); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public void add(int index, Custom element) { 
     this.prepareAdd(element); 
     super.add(index, element); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public Custom remove(int index) { 
     Custom custom = super.remove(index); 
     this.prepareRemove(custom); 
     return custom; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public void clear() { 
     super.clear(); 
     this.namesMap.clear(); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public boolean addAll(Collection<? extends Custom> c) { 
     this.prepareAddAll(c); 
     return super.addAll(c); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public boolean addAll(int index, Collection<? extends Custom> c) { 
     this.prepareAddAll(c); 
     return super.addAll(index, c); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public boolean remove(Object o) { 
     if (super.remove(o)) { 
      this.prepareRemove((Custom) o); 
      return true; 
     } else { 
      return false; 
     } 
    } 

    private void prepareSet(Custom oldCustom, Custom newCustom) { 
     if (oldCustom != null && !oldCustom.name.equals(newCustom.name)) { 
      this.prepareRemove(oldCustom); 
      this.prepareAdd(newCustom); 
     } 
    } 

    private void prepareAdd(Custom custom) { 
     if (custom != null) { 
      Integer integer = this.namesMap.get(custom.name); 
      this.namesMap.put(custom.name, (integer != null) ? integer + 1 : 1); 
     } 
    } 

    private void prepareAddAll(Collection<? extends Custom> c) { 
     for (Custom custom : c) { 
      this.prepareAdd(custom); 
     } 
    } 

    private void prepareRemove(Custom custom) { 
     if (custom != null) { 
      Integer integer = this.namesMap.get(custom.name); 
      this.namesMap.put(custom.name, (integer != null && integer > 0) ? integer - 1 : 0); 
     } 
    } 
} 

Usage:

package test; 

import java.util.ArrayList; 
import java.util.List; 

public class Test { 

    public static void main(String[] args) { 

     List<Custom> list = new ArrayList<Custom>() {{ 
      add(new Custom("A")); 
      add(new Custom("B")); 
      add(new Custom("C")); 
      add(new Custom("A")); 
      add(new Custom("A")); 
      add(new Custom("B")); 
     }}; 

     CustomArrayBuilder customs = new CustomArrayBuilder(list); 
     Custom custom = new Custom("B"); 
     customs.add(custom); 
     customs.add(custom); 
     customs.remove(custom); 
     customs.remove(custom); 
     customs.remove(custom); 

     System.out.println("A: " + customs.getNameAmount("A")); 
     System.out.println("B: " + customs.getNameAmount("B")); 
     System.out.println("C: " + customs.getNameAmount("C")); 
     System.out.println("Z: " + customs.getNameAmount("Z")); 
     System.out.println("Total different names: " + customs.getDifferentNamesAmount()); 
    } 
} 

uscita:

A: 3 
B: 2 
C: 1 
Z: 0 
Total different names: 3 

Potrebbe essere utile quando si utilizzano spesso le operazioni di conteggio. Nota: Non si dovrebbe cambiare il nome di oggetto personalizzato, dovrebbe essere definitiva:

package test; 

class Custom { 
    public int id; 
    final public String name; 

    public Custom(String name) { 
     this.name = name; 
    } 
} 

Oppure si deve fare qualcosa con la lista anche, quando si modifica il nome di un oggetto personalizzato dalla lista.

7

Ora questo può essere fatto facilmente con Java 8 streams — senza librerie aggiuntive richieste.

List<Custom> list = /*...*/; 
long numMatches = list.stream() 
         .filter(c -> "Tom".equals(c.name)) 
         .count(); 
+0

Va notato che il valore dopo c -> è sempre un booleano – galv

0

È possibile utilizzare count() da Eclipse Collections.

MutableList<Custom> customList = Lists.mutable.empty(); 
int count = customList.count(each -> "Tom".equals(each.getName())); 

Se non è possibile modificare CustomList da List:

List<Custom> customList = new ArrayList<>(); 
int count = ListAdapter.adapt(customList).count(each -> "Tom".equals(each.getName())); 

Se si dispone di un metodo che verifica la presenza di un nome è anche possibile utilizzare countWith():

MutableList<Custom> customList = Lists.mutable.empty(); 
int count = customList.countWith(Custom::isNamed, "Tom"); 

class Custom 
{ 
    public int id; 
    public String name; 

    public boolean isNamed(String nameToCheck) 
    { 
     return nameToCheck.equals(this.name); 
    } 
} 

Nota: I sono un collaboratore di Eclipse Collections.

Problemi correlati