Java List.contains (object con valore di campo uguale a x)

Voglio verificare se un List contiene un object che ha un campo con un determinato valore. Ora, potrei usare un ciclo da controllare e controllare, ma ero curioso di sapere se c’era qualcosa di più efficiente in termini di codice.

Qualcosa di simile a;

 if(list.contains(new Object().setName("John"))){ //Do some stuff } 

So che il codice sopra non fa nulla, è solo per dimostrare più o meno quello che sto cercando di ottenere.

Inoltre, giusto per chiarire, la ragione per cui non voglio usare un ciclo semplice è perché questo codice andrà attualmente all’interno di un ciclo che si trova all’interno di un ciclo che si trova all’interno di un ciclo. Per motivi di leggibilità, non voglio continuare ad aggiungere loop a questi loop. Quindi mi sono chiesto se esistessero alternative semplici (ish).

Flussi

Se stai usando Java 8, potresti provare qualcosa del genere:

 public boolean containsName(final List list, final String name){ return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent(); } 

In alternativa, puoi provare qualcosa come questo:

 public boolean containsName(final List list, final String name){ return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent(); } 

Questo metodo restituirà true se l’ List contiene un object MyObject con il nome del name . Se vuoi eseguire un’operazione su ognuno dei MyObject che getName().equals(name) MyObject getName().equals(name) , puoi provare qualcosa del genere:

 public void perform(final List list, final String name){ return list.stream().filter(o -> o.getName().equals(name)).forEach( o -> { //... } ); } 

Dove o rappresenta un’istanza MyObject .

Hai due scelte.

1. La prima scelta, che è preferibile, è di sovrascrivere il metodo `equals ()` nella class Object.

Diciamo, per esempio, che hai questa class Object:

 public class MyObject { private String name; private String location; //getters and setters } 

Ora diciamo che ti interessa solo il nome di MyObject, che dovrebbe essere unico quindi se due `MyObject`s hanno lo stesso nome dovrebbero essere considerati uguali. In tal caso, si dovrebbe sovrascrivere il metodo `equals ()` (e anche il metodo `hashcode ()` in modo che esso paragoni i nomi per determinare l’uguaglianza.

Una volta che hai fatto questo, puoi verificare se una collezione contiene un object MyObject con il nome “pippo” in questo modo:

 MyObject object = new MyObject(); object.setName("foo"); collection.contains(object); 

Tuttavia, questa potrebbe non essere un’opzione per te se:

  • Stai utilizzando sia il nome che la posizione per controllare l’uguaglianza, ma vuoi solo controllare se una collezione ha un `MyObject`s con una certa posizione. In questo caso, hai già sovrascritto `equals ()`.
  • `MyObject` fa parte di un’API che non hai la libertà di cambiare.

Se uno di questi è il caso, ti consigliamo l’opzione 2:

2. Scrivi il tuo metodo di utilità:

 public static boolean containsLocation(Collection c, String location) { for(MyObject o : c) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

In alternativa, è ansible estendere ArrayList (o qualche altra raccolta) e quindi aggiungere il proprio metodo ad esso:

 public boolean containsLocation(String location) { for(MyObject o : this) { if(o != null && o.getLocation.equals(location)) { return true; } } return false; } 

Sfortunatamente non c’è un modo migliore per aggirarlo.

Google Guava

Se stai usando Guava , puoi adottare un approccio funzionale e fare quanto segue

 FluentIterable.from(list).find(new Predicate() { public boolean apply(MyObject input) { return "John".equals(input.getName()); } }).Any(); 

che sembra un po ‘prolisso. Tuttavia, il predicato è un object e puoi fornire diverse varianti per ricerche diverse. Nota come la libreria stessa separa l’iterazione della collezione e la funzione che desideri applicare. Non è necessario sovrascrivere equals() per un comportamento particolare.

Come indicato di seguito, il framework java.util.Stream incorporato in Java 8 e versioni successive fornisce qualcosa di simile.

Ricerca binaria

Puoi utilizzare Collections.binarySearch per cercare un elemento nel tuo elenco (assumendo che l’elenco sia ordinato):

 Collections.binarySearch(list, new YourObject("a1", "b", "c"), new Comparator() { @Override public int compare(YourObject o1, YourObject o2) { return o1.getName().compareTo(o2.getName()); } }); 

che restituirà un numero negativo se l’object non è presente nella collezione altrimenti restituirà l’ index dell’object. Con questo puoi cercare oggetti con diverse strategie di ricerca.

Collection.contains() viene implementato chiamando equals() su ciascun object finché non si restituisce true .

Quindi, un modo per implementare questo è sovrascrivere equals() ma, ovviamente, si può avere solo un uguale.

I framework come Guava quindi usano predicati per questo. Con Iterables.find(list, predicate) , è ansible cercare campi arbitrari inserendo il test nel predicato.

Altri linguaggi costruiti sulla parte superiore della macchina virtuale sono integrati. In Groovy , ad esempio, scrivi semplicemente:

 def result = list.find{ it.name == 'John' } 

Anche Java 8 ha reso la vita più facile:

 List result = list.stream() .filter(it -> "John".equals(it.getName()) .collect(Collectors.toList()); 

Se ti importa di cose del genere, ti suggerisco di leggere il libro “Oltre Java”. Contiene molti esempi delle numerose carenze di Java e di come altre lingue funzionano meglio.

Collezioni Eclipse

Se si utilizzano raccolte Eclipse , è ansible utilizzare il metodo anySatisfy() . ListAdapter tua List in un ListAdapter o cambia la tua List in una ListIterable se ansible.

 ListIterable list = ...; boolean result = list.anySatisfy(myObject -> myObject.getName().equals("John")); 

Se eseguirai operazioni di questo tipo frequentemente, è meglio estrarre un metodo che risponda se il tipo ha l’attributo.

 public class MyObject { private final String name; public MyObject(String name) { this.name = name; } public boolean named(String name) { return Objects.equals(this.name, name); } } 

È ansible utilizzare la forma alternativa anySatisfyWith() insieme a un riferimento al metodo.

 boolean result = list.anySatisfyWith(MyObject::named, "John"); 

Se non riesci a modificare la tua List in una ListIterable , ecco come utilizzeresti ListAdapter .

 boolean result = ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John"); 

Nota: sono un committer per le ollections di Eclipse.

Carta geografica

È ansible creare una Hashmap utilizzando uno dei valori come chiave e quindi vedere se il yourHashMap.keySet().contains(yourValue) restituisce true.

Predicate

Se non usi Java 8 o una libreria che ti offre più funzionalità per gestire le raccolte, potresti implementare qualcosa che può essere più riusabile della tua soluzione.

 interface Predicate{ boolean contains(T item); } static class CollectionUtil{ public static  T find(final Collection collection,final Predicate predicate){ for (T item : collection){ if (predicate.contains(item)){ return item; } } return null; } // and many more methods to deal with collection } 

sto usando qualcosa del genere, ho un’interfaccia predicato e sto passando l’implementazione alla mia class util.

Qual è il vantaggio di farlo a modo mio? hai un metodo che si occupa della ricerca in qualsiasi tipo di raccolta. e non devi creare metodi separati se vuoi cercare per campo diverso. tutto ciò che devi fare è fornire un predicato diverso che può essere distrutto non appena non è più utile /

se vuoi usarlo, tutto ciò che devi fare è chiamare il metodo e definire il tuo predicato

 CollectionUtil.find(list, new Predicate{ public boolean contains(T item){ return "John".equals(item.getName()); } }); 

contains usi di metodo equals internamente. Quindi è necessario sovrascrivere il metodo di equals per la class in base alle proprie esigenze.

Btw questo non sembra sinteticamente corretto:

 new Object().setName("John") 

Se è necessario eseguire questo List.contains(Object with field value equal to x) ripetutamente, una soluzione semplice ed efficiente sarebbe:

 List fieldOfInterestValues = new ArrayList; for(Object obj : List) { fieldOfInterestValues.add(obj.getFieldOfInterest()); } 

Quindi il List.contains(Object with field value equal to x) avrebbe lo stesso risultato di fieldOfInterestValues.contains(x);

Nonostante JAVA 8 SDK ci sono molte librerie di strumenti di raccolta che possono aiutarti a lavorare, ad esempio: http://commons.apache.org/proper/commons-collections/

 Predicate condition = new Predicate() { boolean evaluate(Object obj) { return ((Sample)obj).myField.equals("myVal"); } }; List result = CollectionUtils.select( list, condition ); 

Ecco una soluzione utilizzando Guava

 private boolean checkUserListContainName(List userList, final String targetName){ return FluentIterable.from(userList).anyMatch(new Predicate() { @Override public boolean apply(@Nullable User input) { return input.getName().equals(targetName); } }); }