Lista de Java.contiene (Objeto con valor de campo igual a x)


Quiero comprobar si un List contiene un objeto que tiene un campo con un valor determinado. Ahora, podría usar un bucle para ir a través y comprobar, pero tenía curiosidad si había algo más eficiente código.

Algo como;

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

Sé que el código anterior no hace nada, es solo para demostrar aproximadamente lo que estoy tratando de lograr.

También, solo para aclarar, la razón por la que no quiero usar un bucle simple es porque este código irá actualmente dentro de un bucle que está dentro de un bucle que está dentro de un bucle. Para la legibilidad no quiero seguir añadiendo bucles a estos bucles. Así que me pregunté si había alguna alternativa simple(ish).

Author: Rudi Kershaw, 2013-09-17

12 answers

Flujos

Si está utilizando Java 8, tal vez podría intentar algo como esto:

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

O alternativamente, podrías intentar algo como esto:

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

Este método devolverá true si el List<MyObject> contiene un MyObject con el nombre name. Si desea realizar una operación en cada uno de los MyObjects que getName().equals(name), entonces podría intentar algo como esto:

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

Donde o representa una instancia MyObject.

 154
Author: Josh M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 02:35:14

Tienes dos opciones.

1. La primera opción, que es preferible, es anular el método` equals () ' en su clase de objeto.

Digamos, por ejemplo, que tienes esta clase de objeto:

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

Ahora digamos que solo le importa el nombre del miObjeto, que debe ser único, por lo que si dos `miObjeto ' tienen el mismo nombre deberían considerarse iguales. En ese caso, querrás anular el método` equals () ' (y también el método` hashcode ()') para que compara los nombres para determinar la igualdad.

Una vez hecho esto, puedes comprobar si una Colección contiene un myObject con el nombre "foo" de la siguiente manera:

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

Sin embargo, esto podría no ser una opción para usted si:

  • Está utilizando tanto el nombre como la ubicación para verificar la igualdad, pero solo desea verificar si una Colección tiene algún `myObject ' con una ubicación determinada. En este caso, ya has anulado `equals()`.
  • 'myObject' es parte de una API que no tienes libertad para cambiar.

Si cualquiera de estos es el caso, usted querrá opción 2:

2. Escriba su propio método de utilidad:

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

Alternativamente, puede extender ArrayList (o alguna otra colección) y luego agregar su propio método:

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

Desafortunadamente no hay una mejor manera de evitarlo.

 73
Author: James Dunn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-17 15:20:16

Google Guayaba

Si está utilizando Guayaba , puede tomar un enfoque funcional y hacer lo siguiente

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

Que parece un poco prolijo. Sin embargo, el predicado es un objeto y puede proporcionar diferentes variantes para diferentes búsquedas. Observe cómo la propia biblioteca separa la iteración de la colección y la función que desea aplicar. No tienes que anular equals() para un comportamiento en particular.

Como se indica a continuación, el java.útil.Stream framework integrado en Java 8 y posteriores proporciona algo similar.

 24
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 02:40:11

Búsqueda binaria

Puede usar Colecciones.binarySearch para buscar un elemento en su lista (suponiendo que la lista esté ordenada):

Collections.binarySearch(list, new YourObject("a1", "b",
                "c"), new Comparator<YourObject>() {

            @Override
            public int compare(YourObject o1, YourObject o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

Que devolverá un número negativo si el objeto no está presente en la colección o bien devolverá el index del objeto. Con esto puedes buscar objetos con diferentes estrategias de búsqueda.

 18
Author: Debojit Saikia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 02:33:19

Collection.contains() se implementa llamando a equals() en cada objeto hasta que uno devuelve true.

Así que una forma de implementar esto es anular equals() pero por supuesto, solo se puede tener uno igual.

Frameworks como Guava por lo tanto usan predicados para esto. Con Iterables.find(list, predicate), puede buscar campos arbitrarios poniendo la prueba en el predicado.

Otros lenguajes construidos encima de la VM tienen esto incorporado. En Groovy, por ejemplo, simplemente escribe:

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

Java 8 hizo todas nuestras vidas más fáciles, también:

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

Si te importan cosas como esta, te sugiero el libro "Beyond Java". Contiene muchos ejemplos de las numerosas deficiencias de Java y cómo otros lenguajes hacen mejor.

 16
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-08 13:16:28

Colecciones de eclipses

Si estás usando Colecciones de eclipse, puedes usar el método anySatisfy(). Puede adaptar su List en un ListAdapter o cambiar su List en un ListIterable si es posible.

ListIterable<MyObject> list = ...;

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

Si va a hacer operaciones como esta con frecuencia, es mejor extraer un método que responda si el tipo tiene el atributo.

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);
    }
}

Puede utilizar la forma alternativa anySatisfyWith() junto con una referencia de método.

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

Si no puede cambiar su List en a ListIterable, así es como usarías ListAdapter.

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

Nota: Soy un committer para ollections de Eclipse.

 6
Author: Craig P. Motlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 12:58:56

Mapa

Puede crear un Hashmap<String, Object> usando uno de los valores como clave, y luego ver si yourHashMap.keySet().contains(yourValue) devuelve true.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-10 11:19:58

Predicate

Si no utiliza Java 8, o biblioteca que le da más funcionalidad para tratar con colecciones, podría implementar algo que puede ser más reutilizable que su solución.

interface Predicate<T>{
        boolean contains(T item);
    }

    static class CollectionUtil{

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

Estoy usando algo así, tengo interfaz de predicado, y estoy pasando la implementación a mi clase util.

¿Cuál es la ventaja de hacer esto a mi manera? usted tiene un método que se ocupa de la búsqueda en cualquier tipo de colección. y no tienes que crear separado métodos si desea buscar por campo diferente. todo lo que hay que hacer es proporcionar predicado diferente que puede ser destruido tan pronto como ya no es útil /

Si desea usarlo, todo lo que necesita hacer es llamar al método y definir su predicado

CollectionUtil.find(list, new Predicate<MyObject>{
    public boolean contains(T item){
        return "John".equals(item.getName());
     }
});
 4
Author: user902383,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 02:32:43

contains el método utiliza equals internamente. Por lo tanto, necesita anular el método equals para su clase según su necesidad.

Por cierto esto no parece sintáticamente correcto:

new Object().setName("John")
 2
Author: Juned Ahsan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-17 14:06:27

Si necesita realizar esto List.contains(Object with field value equal to x) repetidamente, una solución simple y eficiente sería:

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

Entonces el List.contains(Object with field value equal to x) tendría el mismo resultado que fieldOfInterestValues.contains(x);

 1
Author: Magda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-16 12:24:15

A pesar de JAVA 8 SDK hay una gran cantidad de bibliotecas de herramientas de colección que pueden ayudarlo a trabajar, por ejemplo: 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 );
 1
Author: Pasha Gharibi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-22 05:34:27

Aquí hay una solución usando Guayaba

private boolean checkUserListContainName(List<User> userList, final String targetName){

    return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
        @Override
        public boolean apply(@Nullable User input) {
            return input.getName().equals(targetName);
        }
    });
}
 0
Author: Phan Van Linh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-21 02:55:33