webentwicklung-frage-antwort-db.com.de

Suchen aller Objekte mit einer bestimmten Eigenschaft in einer Auflistung

Ich habe ein kompliziertes Objekt, wie eine Katze, die viele Eigenschaften hat, wie Alter, Lieblingskatzenfutter und so weiter.

Ein Haufen Katzen ist in einer Java Sammlung gespeichert, und ich muss alle Katzen im Alter von 3 Jahren oder diejenigen, deren Lieblingskatzenfutter Whiskas ist, finden. Sicherlich kann ich eine benutzerdefinierte Methode schreiben, die diese Katzen mit einer bestimmten Eigenschaft findet, aber dies wird mit vielen Eigenschaften umständlich. Gibt es eine generische Möglichkeit, dies zu tun?

85
Jake

Sie könnten eine Methode schreiben, die eine Instanz einer Schnittstelle verwendet, die eine check(Cat) -Methode definiert, wobei diese Methode mit jeder gewünschten Eigenschaftsprüfung implementiert werden kann.

Besser noch, machen Sie es allgemein:

public interface Checker<T> {
    public boolean check(T obj);
}

public class CatChecker implements Checker<Cat> {
    public boolean check(Cat cat) {
        return (cat.age == 3); // or whatever, implement your comparison here
    }
}

// put this in some class
public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
    LinkedList<T> l = new LinkedList<T>();
    for (T obj : coll) {
         if (chk.check(obj))
             l.add(obj);
    }
    return l;
}

Natürlich, wie andere Leute sagen, ist dies das, wofür relationale Datenbanken gemacht wurden ...

45
David Z

Probieren Sie die Commons Collections API aus:

List<Cat> bigList = ....; // master list

Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood().equals("Wiskas") 
            && c.getWhateverElse().equals(Something);
    }
});

Natürlich müssen Sie keine anonyme Klasse verwenden jedes Mal Sie könnten Implementierungen der Predicate -Schnittstelle für häufig verwendete Suchvorgänge erstellen.

62
Brian Dilley

Ich habe Google Collections (jetzt Guava ) für diese Art von Problem verwendet. Es gibt eine Klasse namens Iterables, die eine Schnittstelle namens Predicate als Parameter einer Methode verwenden kann, die wirklich hilfreich ist.

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    public boolean apply(Cat arg) { return arg.age() == 3; }
});

Überprüfen Sie es hier !

55
rcouto

Mit Java 8 lambda expression können Sie so etwas tun

cats.stream()
    .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
    .collect(Collectors.toList());

Konzeptionell identisch mit dem Guava Predicate-Ansatz, sieht jedoch mit Lambda viel sauberer aus

Wahrscheinlich keine gültige Antwort für OP, aber für Menschen mit ähnlichen Bedürfnissen erwähnenswert. :)

53
Adrian Shum

Ich schlage vor, Jxpath zu verwenden, um Abfragen in Objektdiagrammen so durchzuführen, als ob es xpath gefallen würde

JXPathContext.newContext(cats).
     getValue("//*[@drinks='milk']")
11
flybywire

Wieder mit der Commons Collections-API: Sie erhalten den Typcode "checker", wenn Sie das Prädikat separat implementieren:

public class CatPredicate implements Predicate {

    private int age; 


    public CatPredicate(int age) {
        super();
        this.age = age;
    }


    @Override
    public boolean evaluate(Object o) {
        Cat c (Cat)o;
        return c.getAge()==this.age;
    }

}

was verwendet wird als: -

CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
5
user151888

Sie können lambdaj verwenden. Dinge wie diese sind trivial, die Syntax ist wirklich flüssig:

Person me = new Person("Mario", "Fusco", 35);
Person luca = new Person("Luca", "Marrocco", 29);
Person biagio = new Person("Biagio", "Beatrice", 39);
Person celestino = new Person("Celestino", "Bellone", 29);
List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);

und Sie können viel kompliziertere Dinge tun. Für die Matcher wird hamcrest verwendet. Einige werden argumentieren, dass dies kein Java-Stil ist, aber es macht Spaß, wie dieser Typ Java gedreht hat, um ein bisschen funktionale Programmierung zu machen. Schauen Sie sich auch den Quellcode an, es ist ziemlich Science-Fiction.

4
Gismo Ranas

Nur zu Ihrer Information, es gibt 3 andere Antworten auf diese Frage, die Guave verwenden, aber keine Antwort auf die Frage. Der Fragesteller hat angegeben, dass er alle Katzen mit einer passenden Eigenschaft finden möchte, z. Alter von 3. Iterables.find stimmt nur mit einem überein, falls vorhanden. Sie müssten Iterables.filter um dies zu erreichen, wenn Sie Guava verwenden, zum Beispiel:

Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
    @Override
    public boolean apply(Cat input) {
        return input.getAge() == 3;
    }
});
3
Breeno

Commons-Sammlungen verwenden:

EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);

Lösungen, die auf Prädikaten, Filtern und benutzerdefinierten Iteratoren/Komparatoren basieren, sind in Ordnung, bieten jedoch keinen analogen Datenbankindex. Zum Beispiel: Ich möchte eine Sammlung von Katzen auf verschiedene Arten durchsuchen: nach Geschlecht und Alter und nach Alter, so sieht es aus wie zwei Indizes: 1) [Geschlecht, Alter] 2) [Alter]. Werte, auf die von diesen Indizes zugegriffen wird, können gehasht werden, um eine schnelle Suche zu implementieren, ohne die gesamte Auflistung zu iterieren. Gibt es irgendwo eine solche Lösung?

1

Verwenden Sie Google Guava.

final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
    @Override
    public boolean apply(@Nullable Clazz input) {
      return input.getSeq() == lastSeq;
    }
});

Ich denke, diese Methode zu verwenden.

1
Honeymon

Klingt nach etwas, für das Sie LINQ in .NET verwenden würden

Obwohl es für Java) noch keine "echte" LINQ-Implementierung gibt, möchten Sie vielleicht einen Blick auf Quaere werfen, das tun könnte, was Sie leicht genug beschreiben.

1
Eric Petroelje

Sie können so etwas wie JoSQL verwenden und 'SQL' für Ihre Sammlungen schreiben: http://josql.sourceforge.net/

Was wie das klingt, was Sie wollen, mit dem zusätzlichen Vorteil, dass Sie kompliziertere Abfragen durchführen können.

1
duwanis

Sie können einen Teil des generischen Codes im Apache Commons-Projekt ausprobieren. Das Unterprojekt Collections enthält Code zum Suchen von Objekten, die einem bestimmten Prädikat entsprechen, sowie einer großen Anzahl von Prädikaten (equals, null, instanceof usw.). Mit dem Unterprojekt BeanUtils können Sie Prädikate erstellen, mit denen die Eigenschaften von Beans getestet werden.

Verwenden Sie die Klasse CollectionUtils , um innerhalb einer Sammlung zu suchen. Hierfür gibt es einige Methoden, insbesondere aber die select () -Methode. Verwenden Sie die folgenden Klassen, um Prädikate zu erstellen, oder schreiben Sie Ihre eigenen: Predicate , PredicateUtils , BeanPredicate .

Das ist alles manchmal etwas umständlich, aber immerhin ist es generisch! :-)

1
Rich Dougherty

JFilter http://code.google.com/p/jfilter/ entspricht Ihren Anforderungen.

JFilter ist eine einfache und leistungsstarke Open-Source-Bibliothek, mit der eine Sammlung von Java Beans abgefragt werden kann.

Hauptmerkmale

  • Unterstützung von Collection-Eigenschaften (Java.util.Collection, Java.util.Map und Array).
  • Unterstützung der Sammlung innerhalb der Sammlung beliebiger Tiefe.
  • Unterstützung innerer Abfragen.
  • Unterstützung von parametrisierten Abfragen.
  • Kann 1 Million Datensätze in wenigen 100 ms filtern.
  • Filter (Abfrage) wird im einfachen json-Format angegeben, ähnlich wie Mangodb-Abfragen. Es folgen einige Beispiele.
    • {"id": {"$ le": "10"}
      • dabei ist die Eigenschaft object id kleiner als 10.
    • {"id": {"$ in": ["0", "100"]}}
      • dabei ist die Objekt-ID-Eigenschaft 0 oder 100.
    • {"lineItems": {"lineAmount": "1"}}
      • dabei hat die lineItems-Auflistungseigenschaft des parametrisierten Typs lineAmount den Wert 1.
    • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
      • dabei ist die Eigenschaft id 0 und die Eigenschaft billingAddress.city ist DEL.
    • {"lineItems": {"tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}
      • wobei die lineItems-Auflistungseigenschaft des parametrisierten Typs, die die Steuerkartentypeigenschaft des parametrisierten Typs aufweist, Code aufweist, der einem GST-Wert größer als 1,01 entspricht.
    • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
      • Wählen Sie alle Produkte mit einem Produktcode von 10 oder einem Artikelpreis von 20 und 40 und einem Artikelcode von "RedApple".
0
Kamran Ali Khan

sie können Elemente aus einer Liste wie folgt suchen. Viel Glück!

int _searchList(List<T> list,T item) {
int idx = -1;
idx = Collections.binarySearch(list,item,
        new Comparator<T>() {
            public int compare(Titm1,
                    Titm2) {

                // key1
                if (itm1.key1.compareTo(itm2.key1) != 0) {
                    return itm1.key2.compareTo(itm2.key2);
                }

                // key2
                return itm1.key2
                        .compareTo(itm2.key2);
            }
        });

return idx;

}

0
han

Sie können diese Objekte in einer Datenbank speichern. Wenn Sie den Overhead eines vollwertigen Datenbankservers nicht benötigen, können Sie einen eingebetteten Server wie HSQLDB verwenden. Anschließend können Sie Hibernate oder BeanKeeper (einfacher zu verwenden) oder ein anderes ORM verwenden, um Objekte Tabellen zuzuordnen. Sie verwenden weiterhin das Modell OO) und nutzen die erweiterten Speicher- und Abfragefunktionen einer Datenbank.

0
Cesar

Guave hat eine sehr leistungsstarke Suchfunktion, wenn es um solche Probleme geht. Wenn Ihr Bereich beispielsweise ein Objekt basierend auf einer seiner Eigenschaften sucht, können Sie Folgendes in Betracht ziehen:

Iterables.tryFind(listOfCats, new Predicate<Cat>(){
    @Override
    boolean apply(@Nullable Cat input) {
        return "tom".equalsIgnoreCase(input.name());
    }
}).or(new Cat("Tom"));

falls es möglich ist, dass die Tom-Katze nicht in der listOfCats enthalten ist, wird sie zurückgegeben, sodass Sie NPE vermeiden können.

0
Ivan Hristov

Hier ist eine Idee für eine Sucherklasse, die Sie mit den spezifischen Werten parametrisieren können, nach denen Sie suchen möchten.

Sie können auch weiter gehen und die Namen der Eigenschaften speichern, wahrscheinlich in einer Karte mit den gewünschten Werten. In diesem Fall würden Sie die Reflektion der Cat-Klasse verwenden, um die entsprechenden Methoden mit den Eigenschaftsnamen aufzurufen.

public class CatSearcher {

  private Integer ageToFind = null;
  private String  foodToFind = null;

  public CatSearcher( Integer age, String food ) {
    this.ageToFind = age;
    this.foodToFind = food;
  }

  private boolean isMatch(Cat cat) {
    if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
       return false;
    }
    if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
       return false;
    }
    return true;
  }

  public Collection<Cat> search( Collection<Cat> listToSearch ) {
    // details left to the imagination, but basically iterate over
    // the input list, call isMatch on each element, and if true
    // add it to a local collection which is returned at the end.
  }

}
0
Dave Costa

Ein sehr häufiges Problem und ich habe Google-Sammlungen verwendet und hier ist mein Code

public class FindByIdPredicate implements Predicate<IDObject> {

private Long entityId;

public FindByIdPredicate(final Long entityId) {
    this.entityId = entityId;

}

@Override
public boolean apply(final IDObject input) {
    return input.getId().equals(this.entityId);
}

/**
     * Pass in the Collection
 * @param Collection
 * @return IdObject if present or null
 */
public IDObject getEntity(final Collection<? extends IDObject> collection) {

    for (IDObject idObject : collection) {
        if (this.apply(idObject)) {
            return idObject;
        }
    }
    return null;

}

/**
 * @param Set
 * @return IdObject if present or null
 */
@SuppressWarnings("unchecked")
public <T> T getEntity(final Set<? extends IDObject> set) {

    for (IDObject idObject : set) {
        if (this.apply(idObject)) {
            return (T) idObject;
        }
    }
    return null;

}

}

Hoffe das hilft

0
vsingh