Lista Javy.contains (obiekt o wartości pola równej x)

Chcę sprawdzić, czy List zawiera obiekt, który ma pole z określoną wartością. Teraz, mógłbym użyć pętli, aby przejść i sprawdzić, ale byłem ciekaw, czy jest coś bardziej wydajnego kodu.

Coś jak;

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

Wiem, że powyższy kod nic nie robi, jest po prostu po to, aby pokazać mniej więcej to, co staram się osiągnąć.

Również, tak dla jasności, powodem, dla którego nie chcę używać prostej pętli, jest to, że ten kod będzie obecnie wchodził do pętli, która jest wewnątrz pętli, która jest wewnątrz pętli. Dla czytelności nie chcę dodawać pętli do tych pętli. Więc zastanawiałem się, czy istnieją jakieś proste(ish) alternatywy.

Author: Rudi Kershaw, 2013-09-17

12 answers

Strumienie

Jeśli używasz Javy 8, może mógłbyś spróbować czegoś takiego:

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

Lub alternatywnie, możesz spróbować czegoś takiego:

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

Ta metoda zwróci true, Jeśli List<MyObject> zawiera MyObject o nazwie name. Jeśli chcesz wykonać operację na każdym z MyObjects, które getName().equals(name), Możesz spróbować czegoś takiego:

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

Gdzie o reprezentuje MyObject instancję.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 02:35:14

masz dwa wyjścia.

1. Pierwszym wyborem, który jest preferowany, jest nadpisanie metody` equals () ' w klasie obiektu.

Powiedzmy, na przykład, masz klasę obiektu:

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

Załóżmy teraz, że zależy Ci tylko na nazwie MyObject, że powinna być unikalna, więc jeśli dwa 'MyObject' mają tę samą nazwę, powinny być uważane za równe. W takim przypadku chcesz nadpisać metodę` equals ()` (a także metodę `hashcode ()`), aby porównuje nazwy, aby określić równość.

Gdy już to zrobisz, możesz sprawdzić, czy Kolekcja zawiera obiekt MyObject o nazwie "foo" w następujący sposób:

MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
Jednak może to nie być opcja dla Ciebie, Jeśli:
  • używasz zarówno nazwy, jak i lokalizacji, aby sprawdzić równość, ale chcesz tylko sprawdzić, czy kolekcja ma jakieś 'MyObject' z określoną lokalizacją. W tym przypadku nadpisałeś już ' equals ()`.
  • 'MyObject' jest częścią API że nie masz prawa się zmieniać.

Jeśli którykolwiek z tych przypadków, będziesz chciał opcję 2:

2. Napisz własną metodę użytkową:

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

Alternatywnie możesz rozszerzyć ArrayList (lub inną kolekcję), a następnie dodać do niej własną metodę:

public boolean containsLocation(String location) {
    for(MyObject o : this) {
        if(o != null && o.getLocation.equals(location)) {
                return true;
            }
        }
        return false;
    }
Niestety nie ma lepszego sposobu na obejście tego.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-17 15:20:16

Google Guava

Jeśli używasz Guava , możesz zastosować podejście funkcjonalne i wykonać następujące czynności

FluentIterable.from(list).find(new Predicate<MyObject>() {
   public boolean apply(MyObject input) {
      return "John".equals(input.getName());
   }
}).Any();
Co wygląda trochę gadatliwie. Jednak predykat jest obiektem i można podać różne warianty dla różnych wyszukiwań. Zwróć uwagę, jak biblioteka sama oddziela iterację kolekcji od funkcji, którą chcesz zastosować. Nie musisz nadpisywać equals() dla określonego zachowania.

Jak wspomniano poniżej, java.util.Stream framework Wbudowany W Java 8 i późniejsze dostarcza czegoś podobnego.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 02:40:11

Wyszukiwanie Binarne

Możesz używać kolekcji.binarySearch aby wyszukać element na liście (zakładając, że lista jest posortowana):

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

, która zwróci liczbę ujemną, jeśli obiekt nie jest obecny w kolekcji lub zwróci index obiektu. Dzięki temu możesz wyszukiwać obiekty o różnych strategiach wyszukiwania.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 02:33:19

Collection.contains() jest zaimplementowane przez wywołanie equals() na każdym obiekcie, dopóki jeden nie zwróci true.

Więc jednym ze sposobów na zaimplementowanie tego jest nadpisanie equals() ale oczywiście, możesz mieć tylko jedną równą.

Frameworki takie jak Guava używają więc do tego predykatów. Za pomocą Iterables.find(list, predicate) można wyszukiwać dowolne pola, umieszczając test w predykacie.

Inne języki zbudowane na maszynie wirtualnej mają to wbudowane. W Groovy, na przykład, po prostu napisz:

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

Java 8 też ułatwiła nam życie:

List<Foo> result = list.stream()
    .filter(it -> "John".equals(it.getName())
    .collect(Collectors.toList());
Jeśli zależy ci na takich rzeczach, proponuję książkę "Beyond Java". Zawiera wiele przykładów licznych wad Javy i tego, jak inne języki radzą sobie lepiej.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-08 13:16:28

Kolekcje Eclipse

Jeśli używasz kolekcji Eclipse , możesz użyć metody anySatisfy(). Albo dostosować swoje List w ListAdapter lub zmienić swoje List w ListIterable, jeśli to możliwe.

ListIterable<MyObject> list = ...;

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

Jeśli będziesz wykonywać takie operacje często, lepiej wyodrębnić metodę, która odpowie, czy typ ma atrybut.

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

Możesz użyć alternatywnej formy anySatisfyWith() wraz z odniesieniem do metody.

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

Jeśli nie możesz zmienić swojego List na a ListIterable, Oto jak użyjesz ListAdapter.

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

Uwaga: jestem committerem dla Eclipse ollections.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 12:58:56

Mapa

Możesz utworzyć Hashmap<String, Object> używając jednej z wartości jako klucza, a następnie sprawdzić, czy yourHashMap.keySet().contains(yourValue) zwraca 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 11:19:58

Predicate

Jeśli nie używasz Javy 8 lub biblioteki, która daje więcej funkcjonalności do radzenia sobie z kolekcjami, możesz zaimplementować coś, co może być bardziej wielokrotnego użytku niż Twoje rozwiązanie.

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    
    }

Używam czegoś takiego, mam interfejs predicate i przekazuję implementację do mojej klasy util.

Jaka jest korzyść z robienia tego na mój sposób? masz jedną metodę, która zajmuje się wyszukiwaniem w dowolnej kolekcji typów. i nie musisz tworzyć oddzielnych metody, jeśli chcesz wyszukiwać według innego pola. wszystko, co musisz zrobić, to podać inny predykat, który można zniszczyć, gdy tylko przestanie być użyteczny/

Jeśli chcesz go użyć, wszystko, co musisz zrobić, to wywołać metodę i zdefiniować predykat tyour

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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 02:32:43

contains Metoda wykorzystuje equals wewnętrznie. Więc musisz zastąpić metodę equals dla swojej klasy zgodnie z potrzebami.

Btw to nie wygląda składniowo poprawnie:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-17 14:06:27

Jeśli trzeba wykonać to List.contains(Object with field value equal to x) wielokrotnie, proste i skuteczne obejście będzie następujące:

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

Wtedy List.contains(Object with field value equal to x) będzie miał taki sam wynik jak 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-16 12:24:15

Pomimo JAVA 8 SDK istnieje wiele bibliotek narzędzi, które mogą pomóc w pracy, na przykład: 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 05:34:27

Oto rozwiązanie za pomocą guawa

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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-21 02:55:33