it-swarm.com.de

Der schnellste Weg, um zu überprüfen, ob eine Liste <String> einen eindeutigen String enthält

Grundsätzlich habe ich ungefähr 1.000.000 Zeichenfolgen. Für jede Anfrage muss ich überprüfen, ob ein String zur Liste gehört oder nicht.

Ich mache mir Sorgen um die Leistung. Was ist die beste Methode? ArrayList? Hash?

62
Ben

Am besten verwenden Sie eine HashSet und überprüfen Sie mit der contains()-Methode, ob ein String im Set vorhanden ist. HashSets sind für den schnellen Zugriff mithilfe der Objektmethoden hashCode() und equals() ausgelegt. Der Javadoc für HashSet besagt:

Diese Klasse bietet eine konstante Zeitleistung für die grundlegenden Vorgänge (Hinzufügen, Entfernen, Inhalt und Größe).

HashSet speichert Objekte in Hash-Buckets was bedeutet, dass der von der hashCode-Methode zurückgegebene Wert bestimmt, in welchem ​​Bucket ein Objekt gespeichert ist. Auf diese Weise wird die Anzahl der Gleichheitsüberprüfungen geprüft, die die HashSet über die equals()-Methode durchführen muss reduziert auf nur die anderen Objekte im selben Hash-Bucket.

Um HashSets und HashMaps effektiv verwenden zu können, müssen Sie den equals- und hashCode-Kontrakt im Javadoc einhalten. Im Fall von Java.lang.String wurden diese Methoden bereits implementiert.

93
krock

Im Allgemeinen bietet ein HashSet eine bessere Leistung, da es nicht wie bei einer ArrayList jedes Element durchsehen und vergleichen muss, sondern meist nur einige Elemente vergleicht, bei denen die Hashcodes gleich sind.

Für 1M-Strings ist die Leistung von hashSet jedoch möglicherweise nicht optimal. Viele Cache-Fehler werden beim Suchen des Satzes langsamer. Wenn alle Zeichenfolgen gleich wahrscheinlich sind, ist dies unvermeidlich. Wenn jedoch einige Zeichenfolgen häufiger als andere angefordert werden, können Sie die allgemeinen Zeichenfolgen in einem kleinen Hash-Set platzieren und dies zuerst prüfen, bevor Sie die größere Menge prüfen. Der kleine Hashsatz sollte so bemessen sein, dass er in den Cache passt (z. B. höchstens einige hundert K). Hits auf den kleinen Hashsatz sind dann sehr schnell, während Hits auf den größeren Hashsatz mit einer durch die Speicherbandbreite begrenzten Geschwindigkeit voranschreiten.

11
mdma

Bevor Sie fortfahren, sollten Sie Folgendes bedenken: Warum machen Sie sich Sorgen um die Leistung? Wie oft wird diese Prüfung aufgerufen?

Wie für mögliche Lösungen:

  • Wenn die Liste bereits sortiert ist, können Sie Java.util.Collections.binarySearch verwenden, das dieselben Leistungsmerkmale wie ein Java.util.TreeSet bietet.

  • Ansonsten können Sie einen Java.util.HashSet, der als Leistungsmerkmal von O (1) verwendet wird. Beachten Sie, dass die Berechnung des Hash-Codes für eine Zeichenfolge, für die noch keine berechnet wurde, eine Operation O(m) mit m = string.length() ist. Denken Sie auch daran, dass Hashtabellen nur gut funktionieren, wenn sie einen bestimmten Lastfaktor erreichen, d. H. Hashtabeln verwenden mehr Speicher als einfache Listen. Der von HashSet verwendete Standardladefaktor ist 0,75. Dies bedeutet, dass intern ein HashSet für 1e6-Objekte ein Array mit 1.3e6-Einträgen verwendet.

  • Wenn das HashSet für Sie nicht funktioniert (z. B. weil es viele Hash-Kollisionen gibt, weil der Speicher knapp ist oder weil es viele Einfügungen gibt), sollten Sie ein Trie verwenden. Die Suche in einem Trie hat eine Worst-Case-Komplexität von O(m), wobei m = string.length() ist. Ein Trie hat auch einige zusätzliche Vorteile, die für Sie nützlich sein können: Sie können beispielsweise am besten passende für eine Suchzeichenfolge angeben. Denken Sie jedoch daran, dass der beste Code kein Code ist. Rollen Sie also Ihre eigene Trie-Implementierung nur dann, wenn der Nutzen die Kosten übersteigt.

  • Erwägen Sie die Verwendung einer Datenbank, wenn Sie komplexere Abfragen wünschen, z. Übereinstimmung für einen Teilstring oder einen regulären Ausdruck.

8
nd.

Ich würde eine Set verwenden, in den meisten Fällen ist HashSet in Ordnung. 

5
unbeli

Nach der Übung sind hier meine Ergebnisse.

private static final int TEST_CYCLES = 4000;
private static final long Rand_ELEMENT_COUNT = 1000000l;
private static final int Rand_STR_LEN = 20;
//Mean time
/*
Array list:18.55425
Array list not contains:17.113
Hash set:5.0E-4
Hash set not contains:7.5E-4
*/

Ich glaube, die Zahlen sprechen für sich. Die Suchzeit des Hash-Sets ist viel schneller.

2
awiebe

Bei so vielen Strings denke ich sofort an einen Trie . Es funktioniert besser mit einer begrenzten Anzahl von Zeichen (wie z. B. Buchstaben) und/oder wenn der Beginn vieler Zeichenfolgen überlappt.

2
ILMTitan

Wenn Sie über eine so große Anzahl von Zeichenfolgen verfügen, können Sie am besten eine Datenbank verwenden. Suchen Sie nach MySQL.

1
oopbase

Vielleicht ist dies für Ihren Fall nicht erforderlich, aber ich denke, es ist nützlich zu wissen, dass es einige platzsparende probabilistische Algorithmen gibt. Zum Beispiel Bloom Filter .

1
simplylizz

Manchmal möchten Sie prüfen, ob sich ein Objekt in der Liste/Menge befindet und gleichzeitig die Liste/Menge bestellt werden soll. Wenn Sie Objekte auch ohne Enumeration oder Iterator problemlos abrufen möchten, können Sie sowohl ArrayList<String> als auch HashMap<String, Integer> verwenden. Die Liste wird durch die Karte gesichert.

Beispiel aus einer Arbeit, die ich kürzlich gemacht habe:

public class NodeKey<K> implements Serializable, Cloneable{
private static final long serialVersionUID = -634779076519943311L;

private NodeKey<K> parent;
private List<K> children = new ArrayList<K>();
private Map<K, Integer> childrenToListMap = new HashMap<K, Integer>();

public NodeKey() {}

public NodeKey(Collection<? extends K> c){
    List<K> childHierarchy = new ArrayList<K>(c);
    K childLevel0 = childHierarchy.remove(0);

    if(!childrenToListMap.containsKey(childLevel0)){
        children.add(childLevel0);
        childrenToListMap.put(childLevel0, children.size()-1);
    }

    ...

In diesem Fall wäre der Parameter K für Sie eine String. Die Karte (childrenToMapList) speichert Strings, die in die Liste (children) eingefügt wurde, als Schlüssel, und die Kartenwerte sind die Indexposition in der Liste.

Der Grund für die Liste und die Karte liegt darin, dass Sie indexierte Werte der Liste abrufen können, ohne eine Iteration über einen HashSet<String> ausführen zu müssen.

0
ghostNet

Nicht nur für String können Sie Set verwenden, wenn Sie eindeutige Elemente benötigen.

Wenn es sich beim Elementtyp um primitive Elemente oder Umhüllungen handelt, ist dies möglicherweise nicht wichtig. Wenn es sich jedoch um eine Klasse handelt, müssen Sie zwei Methoden überschreiben:

  1. hash-Code()
  2. gleich ()
0
Truong Ha