llenar - Java List.contains(Objeto con valor de campo igual a x)
list contains java (12)
Quiero comprobar si una List
contiene un objeto que tiene un campo con un cierto valor. Ahora, podría usar un bucle para revisar y verificar, pero tenía curiosidad de saber si había algo más eficiente con el 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 más o menos lo que estoy tratando de lograr.
Además, solo para aclarar, la razón por la que no quiero usar un bucle simple es porque este código irá dentro de un bucle que está dentro de un bucle que está dentro de un bucle. Para la legibilidad no quiero seguir agregando loops a estos bucles. Entonces me pregunté si habría alguna alternativa simple (ish).
Búsqueda binaria
Puede usar Collections.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 devolverá el index
del objeto. Con esto puedes buscar objetos con diferentes estrategias de búsqueda.
Colecciones Eclipse
Si está utilizando colecciones de Eclipse , puede usar el método anySatisfy()
. ListAdapter
su List
en un ListAdapter
o cambie su List
a ListIterable
si es posible.
ListIterable<MyObject> list = ...;
boolean result =
list.anySatisfy(myObject -> myObject.getName().equals("John"));
Si realiza 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 usar la forma alternativa anySatisfyWith()
junto con una referencia de método.
boolean result = list.anySatisfyWith(MyObject::named, "John");
Si no puede cambiar su List
a una ListIterable
, así es como usaría ListAdapter
.
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
Nota: soy un committer para ollections de Eclipse.
Corrientes
Si está utilizando Java 8, quizás 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 la List<MyObject>
contiene un MyObject
con el nombre del name
. Si desea realizar una operación en cada uno de los MyObject
que getName().equals(name)
, puede 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 de MyObject
.
Google Guava
Si está usando Guava , 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 se ve un poco detallado. Sin embargo, el predicado es un objeto y puede proporcionar diferentes variantes para diferentes búsquedas. Tenga en cuenta cómo la biblioteca separa la iteración de la colección y la función que desea aplicar. No tiene que anular equals()
para un comportamiento particular.
Como se indica a continuación, el marco java.util.Stream integrado en Java 8 y posterior proporciona algo similar.
Mapa
Podría crear un Hashmap<String, Object>
usando uno de los valores como una clave, y luego ver si yourHashMap.keySet().contains(yourValue)
devuelve verdadero.
Predicate
Si no utiliza Java 8, o una biblioteca que le brinda más funcionalidad para tratar con colecciones, podría implementar algo que pueda 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 una interfaz de predicado, y estoy pasando su implementación a mi clase de utilidades.
¿Cuál es la ventaja de hacer esto en mi camino? tienes un método que trata con la búsqueda en cualquier colección de tipos. y no tiene que crear métodos separados si desea buscar por campo diferente. todo lo que debes hacer es proporcionar un predicado diferente que pueda destruirse tan pronto como ya no sea útil /
si quieres usarlo, todo lo que tienes que hacer es llamar al método y definir el predicado tyour
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
A pesar de JAVA 8 SDK, hay muchas herramientas de recopilación que las bibliotecas 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 );
Aquí hay una solución usando Guava
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);
}
});
}
Si necesita realizar este 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, List.contains(Object with field value equal to x)
tendría el mismo resultado que fieldOfInterestValues.contains(x);
Tienes dos opciones.
1. La primera opción, que es preferible, es anular el método `equals ()` en su clase Object.
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 te importa el nombre de MyObject, que debe ser único, por lo que si dos `MyObject`s tienen el mismo nombre, se deben considerar iguales. En ese caso, querrá anular el método `equals ()` (y también el método `hashcode ()`) para que compare los nombres para determinar la igualdad.
Una vez que haya hecho esto, puede verificar si una Colección contiene un objeto MyObject con el nombre "foo" de la siguiente manera:
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
Sin embargo, esta podría no ser una opción para ti si:
- Está utilizando tanto el nombre como la ubicación para verificar la igualdad, pero solo desea verificar si una Colección tiene cualquier `MyObject`s con una determinada ubicación. En este caso, ya has anulado `equals ()`.
- `MyObject` es parte de una API que no tiene libertad de cambiar.
Si cualquiera de estos es el caso, querrá la opción 2:
2. Escribe tu 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 agregarle su propio método:
public boolean containsLocation(String location) {
for(MyObject o : this) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Lamentablemente, no hay una mejor manera de evitarlo.
Collection.contains()
se implementa llamando a equals()
en cada objeto hasta que uno devuelva true
.
Entonces, una forma de implementar esto es anulando equals()
pero por supuesto, solo puedes tener uno igual.
Marcos como Guava por lo tanto usan predicados para esto. Con Iterables.find(list, predicate)
, puede buscar campos arbitrarios al poner la prueba en el predicado.
Otros lenguajes incorporados en la VM lo tienen incorporado. En Groovy , por ejemplo, simplemente escribe:
def result = list.find{ it.name == ''John'' }
Java 8 hizo 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 "Más allá de Java". Contiene muchos ejemplos de las numerosas deficiencias de Java y cómo otros idiomas funcionan mejor.
contains
usos de métodos equals
internamente. Por lo tanto, debe anular el método de equals
para su clase según su necesidad.
Por cierto, esto no parece sintácticamente correcto:
new Object().setName("John")