Ich möchte prüfen, ob eine List
ein Objekt enthält, das ein Feld mit einem bestimmten Wert hat. Jetzt konnte ich eine Schleife verwenden, um durchzugehen und zu überprüfen, aber ich war gespannt, ob es etwas effizienteres für den Code gibt.
So etwas wie;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
Ich weiß, dass der obige Code nichts tut, er soll nur ungefähr zeigen, was ich erreichen möchte.
Der Grund, aus dem ich keine einfache Schleife verwenden möchte, ist, dass dieser Code derzeit in eine Schleife geht, die sich in einer Schleife befindet, die sich in einer Schleife befindet. Aus Gründen der Lesbarkeit möchte ich nicht ständig weitere Loops hinzufügen. Also habe ich mich gefragt, ob es einfache (ish) Alternativen gibt.
Wenn Sie Java 8 verwenden, können Sie Folgendes versuchen:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}
Alternativ können Sie auch so etwas versuchen:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}
Diese Methode gibt true
zurück, wenn der List<MyObject>
eine MyObject
mit dem Namen name
enthält. Wenn Sie eine Operation für jede der MyObject
s dieser getName().equals(name)
ausführen möchten, können Sie Folgendes versuchen:
public void perform(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).forEach(
o -> {
//...
}
);
}
Dabei steht o
für eine MyObject
-Instanz.
Sie haben zwei Möglichkeiten.
1. Die erste Wahl, die zu bevorzugen ist, besteht darin, die Methode `equals () 'in Ihrer Object-Klasse zu überschreiben.
Angenommen, Sie haben beispielsweise diese Objektklasse:
public class MyObject {
private String name;
private String location;
//getters and setters
}
Nehmen wir an, Sie kümmern sich nur um den Namen des MyObject, dh es sollte eindeutig sein. Wenn also zwei MyObjects denselben Namen haben, sollten sie als gleich angesehen werden. In diesem Fall möchten Sie die Methode `equals () '(und auch die Methode' hashcode () ') überschreiben, damit die Namen zur Bestimmung der Gleichheit verglichen werden.
Wenn Sie dies getan haben, können Sie überprüfen, ob eine Collection ein MyObject mit dem Namen "foo" enthält.
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
Dies ist jedoch möglicherweise keine Option für Sie, wenn:
In beiden Fällen sollten Sie die Option 2 wählen:
2. Schreiben Sie Ihre eigene Utility-Methode:
public static boolean containsLocation(Collection<MyObject> c, String location) {
for(MyObject o : c) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Alternativ können Sie ArrayList (oder eine andere Auflistung) erweitern und dann Ihre eigene Methode hinzufügen:
public boolean containsLocation(String location) {
for(MyObject o : this) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Leider gibt es keinen besseren Weg.
Wenn Sie Guava verwenden, können Sie einen funktionalen Ansatz wählen und Folgendes tun
FluentIterable.from(list).find(new Predicate<MyObject>() {
public boolean apply(MyObject input) {
return "John".equals(input.getName());
}
}).Any();
das sieht ein wenig wortreich aus. Das Prädikat ist jedoch ein Objekt und Sie können verschiedene Varianten für verschiedene Suchvorgänge bereitstellen. Beachten Sie, wie die Bibliothek selbst die Iteration der Sammlung und die Funktion, die Sie anwenden möchten, trennt. Sie müssen equals()
nicht für ein bestimmtes Verhalten überschreiben.
Wie unten erwähnt, bietet das in Java 8 und später integrierte Java.util.Stream - Framework etwas Ähnliches.
Collection.contains()
wird implementiert, indem equals()
für jedes Objekt aufgerufen wird, bis eines von true
zurückgegeben wird.
Eine Möglichkeit, dies zu implementieren, besteht darin, equals()
zu überschreiben, aber natürlich können Sie nur ein Gleiches haben.
Frameworks wie Guava verwenden hierfür Prädikate. Mit Iterables.find(list, predicate)
können Sie nach beliebigen Feldern suchen, indem Sie den Test in das Prädikat einfügen.
Andere Sprachen, die auf VM basieren, haben dies eingebaut. In Groovy zum Beispiel schreiben Sie einfach:
def result = list.find{ it.name == 'John' }
Java 8 hat uns auch unser Leben leichter gemacht:
List<Foo> result = list.stream()
.filter(it -> "John".equals(it.getName())
.collect(Collectors.toList());
Wenn Sie sich für solche Dinge interessieren, schlage ich das Buch "Beyond Java" vor. Es enthält viele Beispiele für die zahlreichen Mängel von Java und wie andere Sprachen besser abschneiden.
Sie können Collections.binarySearch verwenden, um ein Element in Ihrer Liste zu durchsuchen (vorausgesetzt, die Liste ist sortiert):
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());
}
});
dies gibt eine negative Zahl zurück, wenn das Objekt nicht in der Auflistung vorhanden ist. Andernfalls wird die index
des Objekts zurückgegeben. Damit können Sie nach Objekten mit unterschiedlichen Suchstrategien suchen.
Sie könnten einen Hashmap<String, Object>
erstellen, indem Sie einen der Werte als Schlüssel verwenden und dann sehen, ob yourHashMap.keySet().contains(yourValue)
true zurückgibt.
So machen Sie es mit Java 8+:
boolean johnExistance = list.stream().anyMatch(o -> o.getName().equals("John"));
Wenn Sie Eclipse Collections verwenden, können Sie die anySatisfy()
-Methode verwenden. Passen Sie entweder Ihre List
in eine ListAdapter
an oder ändern Sie Ihre List
in eine ListIterable
, wenn möglich.
ListIterable<MyObject> list = ...;
boolean result =
list.anySatisfy(myObject -> myObject.getName().equals("John"));
Wenn Sie solche Operationen häufig ausführen, ist es besser, eine Methode zu extrahieren, die angibt, ob der Typ das Attribut hat.
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);
}
}
Sie können das alternative Formular anySatisfyWith()
zusammen mit einer Methodenreferenz verwenden.
boolean result = list.anySatisfyWith(MyObject::named, "John");
Wenn Sie Ihre List
nicht in eine ListIterable
umwandeln können, verwenden Sie ListAdapter
wie folgt.
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
Hinweis: Ich bin ein Committer für Eclipse-Kollektionen.
Predicate
Wenn Sie nicht Java 8 oder eine Bibliothek verwenden, die mehr Funktionen für den Umgang mit Sammlungen bietet, können Sie etwas implementieren, das wiederverwendbar ist als Ihre Lösung.
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
}
ich benutze so etwas, ich habe ein Prädikat-Interface, und ich übergebe die Implementierung an meine util-Klasse.
Was ist der Vorteil, dies auf meine Art zu tun? Sie haben eine Methode, die die Suche in einer beliebigen Typensammlung behandelt. Sie müssen keine separaten Methoden erstellen, wenn Sie nach anderen Feldern suchen möchten. Alles was Sie tun müssen, ist ein anderes Prädikat, das zerstört werden kann, sobald es nicht mehr nützlich ist.
wenn Sie es verwenden möchten, müssen Sie lediglich die Methode call aufrufen und Ihr Prädikat definieren
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
Die contains
-Methode verwendet intern equals
. Daher müssen Sie die equals
-Methode für Ihre Klasse je nach Bedarf überschreiben.
Übrigens sieht das nicht syntaktisch korrekt aus:
new Object().setName("John")
Hier ist eine Lösung mit 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);
}
});
}
Wenn Sie diese List.contains(Object with field value equal to x)
wiederholt ausführen müssen, wäre eine einfache und effiziente Problemumgehung:
List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
fieldOfInterestValues.add(obj.getFieldOfInterest());
}
Dann hätte List.contains(Object with field value equal to x)
dasselbe Ergebnis wie fieldOfInterestValues.contains(x);
Trotz Java 8 SDK gibt es viele Sammlungstools, mit denen Bibliotheken arbeiten können, zum Beispiel: 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 );