web-dev-qa-db-ger.com

Welche Datenstruktur würden Sie verwenden: TreeMap oder HashMap? (Java)

Beschreibung | Ein Java-Programm zum Lesen einer Textdatei und Drucken der einzelnen Wörter in alphabetischer Reihenfolge zusammen mit der Häufigkeit, mit der das Wort im Text vorkommt. 

Das Programm sollte eine Variable vom Typ Map<String, Integer> deklarieren, um die Wörter und die entsprechende Häufigkeit des Auftretens zu speichern. Welcher konkrete Typ aber? TreeMap<String, Number> oder HashMap<String, Number>?

Die Eingabe sollte in Kleinbuchstaben umgewandelt werden.

Ein Word enthält keines der folgenden Zeichen: \t\t\n]f.,!?:;\"()'

Beispielausgabe |  

 Word            Frequency
  a                 1
  and               5
  appearances       1
  as                1
         .
         .
         .

Bemerkung | Ich weiß, ich habe in Perl elegante Lösungen mit ungefähr zwei Codezeilen gesehen. Ich möchte es jedoch in Java sehen. 

Edit: Ach ja, es ist hilfreich, eine Implementierung mit einer dieser Strukturen (in Java) zu zeigen. 

53
JohnZaj

TreeMap scheint mir ein Kinderspiel zu sein - einfach wegen der Anforderung "in alphabetischer Reihenfolge". HashMap hat keine Reihenfolge, wenn Sie es durchlaufen. TreeMap iteriert in der natürlichen Schlüsselreihenfolge.

BEARBEITEN: Ich denke, Konrads Kommentar deutete darauf hin, dass man "HashMap verwenden und dann sortieren" sollte. Das ist gut, denn obwohl wir anfangs N Iterationen haben, haben wir am Ende K <= N Schlüssel aufgrund von Duplikaten. Wir könnten genauso gut das teure Stück (Sortieren) bis zum Ende speichern, wenn wir weniger Schlüssel haben, als den kleinen, aber nicht konstanten Treffer zu nehmen, es sortiert zu halten, während wir gehen.

Trotzdem halte ich im Moment an meiner Antwort fest: weil es der einfachste Weg ist, das Ziel zu erreichen. Wir wissen nicht wirklich, dass das OP besonders um die Leistung besorgt ist, aber die Frage impliziert, dass er sich um die Eleganz und Kürze sorgt. Die Verwendung einer TreeMap macht dies unglaublich kurz, was mich anspricht. Ich vermute, wenn die Leistung wirklich ein Problem darstellt, gibt es möglicherweise eine bessere Möglichkeit, sie anzugreifen als TreeMap oder HashMap :)

59
Jon Skeet

TreeMap schlägt HashMap, da TreeMap bereits für Sie sortiert ist.

Möglicherweise möchten Sie jedoch die Verwendung einer geeigneteren Datenstruktur, einer Tasche, in Betracht ziehen. Siehe Commons Collections - und die TreeBag class:

Dies hat eine Nice-optimierte interne Struktur und API:

bag.add("big")
bag.add("small")
bag.add("big")
int count = bag.getCount("big")

BEARBEITEN: Die Frage nach der Leistung von HashMap vs. TreeMap wurde von Jon - HashMap beantwortet und die Sortierung ist möglicherweise schneller (versuchen Sie es!), Aber TreeBag ist einfacher. Gleiches gilt für Taschen. Es gibt ein HashBag sowie ein TreeBag. Basierend auf der Implementierung (verwendet eine veränderliche Ganzzahl) sollte ein Beutel die entsprechende einfache Karte von Ganzzahl übertreffen. Der einzige Weg, um sicher zu wissen, ist wie bei jeder Leistungsfrage das Testen.

18
JodaStephen

Ich sehe ziemlich viele Leute, die sagen, "TreeMap-Lookup braucht O(n log n)" !! Woher? 

Ich weiß nicht, wie es implementiert wurde, aber in meinem Kopf braucht es O(log n)

Dies liegt daran, dass die Suche in einem Baum in O(log n) erfolgen kann. Sie sortieren nicht jedes Mal den gesamten Baum, wenn Sie ein Element einfügen. Das ist die ganze Idee eines Baumes!

Um auf die ursprüngliche Frage zurückzukommen, erweisen sich die Vergleichszahlen als:

HashMap-Ansatz: O(n + k log k) Durchschnittsfall, der schlimmste Fall könnte viel größer sein

TreeMap-Ansatz: O(k + n log k) Worst-Case

dabei steht n für die Anzahl der Wörter im Text und k für die Anzahl der verschiedenen Wörter im Text.

11
saurabh

Hash Map sollte viel schneller sein. Sie sollten keinen Container auswählen, je nachdem, wie die Elemente schließlich angeordnet werden sollen. Sortieren Sie einfach die Liste der (Word-, Frequenz-) Paare am Ende. In der Regel müssen weniger solche Paare sortiert werden als Wörter in den Dateien. Daher ist die asymptotische (und reelle) Leistung mit einer Hash-Map besser.

3
hashlover

"Wenn ein Schlüssel bereits vorhanden ist, hat er dieselbe Leistung wie eine HashMap." - Das ist einfach falsch. HashMap hat O(1) Einfügung und TreeMap O (n log n). Es wird mindestens n log n Überprüfungen benötigt, um herauszufinden, ob es in der Tabelle steht!

2
coderz

Sie können einer Variablen vom Typ TreeMap<String,Number> keinen Map<String,Integer> zuweisen. Double, Long usw. können in einen TreeMap<String,Number> "eingefügt" werden. Wenn ich einen Wert aus einem Map<String,Integer> "erhalte", muss es eine Integer sein.

I18n-Probleme, Speicherbeschränkungen und Fehlerbehandlung werden vollständig ignoriert.

class Counter {

  public static void main(String... argv)
    throws Exception
  {
    FileChannel fc = new FileInputStream(argv[0]).getChannel();
    ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
    CharBuffer cb = Charset.defaultCharset().decode(bb);
    Pattern p = Pattern.compile("[^ \t\r\n\f.,!?:;\"()']+");
    Map<String, Integer> counts = new TreeMap<String, Integer>();
    Matcher m = p.matcher(cb);
    while (m.find()) {
      String Word = m.group();
      Integer count = counts.get(Word);
      count = (count == null) ? 1 : count + 1;
      counts.put(Word, count);
    }
    fc.close();
    for (Map.Entry<String, Integer> e : counts.entrySet()) {
      System.out.printf("%s: %d%n", e.getKey(), e.getValue());
    }
  }

}
2
erickson
import Java.io.BufferedReader;
import Java.io.DataInputStream;
import Java.io.FileInputStream;
import Java.io.FileNotFoundException;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.io.ObjectInputStream.GetField;
import Java.util.Iterator;
import Java.util.Map;
import Java.util.StringTokenizer;
import Java.util.TreeMap;

public class TreeMapExample {

    public static void main (String args[]){
        Map<String,Integer> tm = new TreeMap<String,Integer>();
        try {

            FileInputStream fis = new FileInputStream("Test.txt");
            DataInputStream in = new DataInputStream(fis);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String line;
            int countValue = 1;
            while((line = br.readLine())!= null ){
                line = line.replaceAll("[-+.^:;,()\"\\[\\]]","");
                StringTokenizer st = new StringTokenizer(line, " ");    
                while(st.hasMoreTokens()){
                    String nextElement = (String) st.nextElement();

                    if(tm.size()>0 && tm.containsKey(nextElement)){
                        int val = 0;
                        if(tm.get(nextElement)!= null){
                        val = (Integer) tm.get(nextElement);
                        val = val+1;
                        }
                        tm.put(nextElement, val);
                    }else{
                    tm.put(nextElement, 1);
                    }

                }
            }
            for(Map.Entry<String,Integer> entry : tm.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
            }

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
2
Balu

Verwenden Sie dazu meiner Meinung nach besser HashBag aus Apache Commons Collections oder HashMultiset aus Guava oder HashBag aus Eclipse Collections (formal) GS Collections ) oder eine der folgenden Klassen:

    Order    |  Guava           |   Apache  | Eclipse(GS) | JDK analog
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Not define   | HashMultiset     |   HashBag | HashBag     | HashMap<String, Integer>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Sorted       | TreeMultiset     |   TreeBag | TreeBag     | TreeMap<String, Integer>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Linked       |LinkedHashMultiset|     -     |     -       | LinkedHashMap<String, Integere>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Concurrent & | ConcurrentHash-  |Synchroniz-|Synchroniz-  | Collections.synchronizedMap(
not define   | Multiset         |   edBag   | edBag       |       HashMap<String, Integer>)
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Concurrent   |         -        |Synchroniz-|Synchroniz-  | Collections.synchronizedSorted-
and sorted   |                  |edSortedBag| edSortedBag |       Map(TreeMap<>))
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Immutable and| ImmutableMultiset|Unmodifiab-|Unmodifiab-  | Collections.unmodifiableMap(
not define   |                  |   leBag   | leBag       | HashMap<String, Integer>)
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Immutable and| ImmutableSorted- |Unmodifiab-|Unmodifiab-  | Collections.unmodifiableSorted-
sorted       | Multiset         |leSortedBag| leSortedBag | Map(TreeMap<String, Integer>))
────────────────────────────────────────────────────────────────────────

Beispiele:

1. SynchronizedSortedBag von Apache verwenden:

    // Parse text to separate words
    String INPUT_TEXT = "Hello World! Hello All! Hi World!";
    // Create Multiset
    Bag bag = SynchronizedSortedBag.synchronizedBag(new TreeBag(Arrays.asList(INPUT_TEXT.split(" "))));

    // Print count words
    System.out.println(bag); // print [1:All!,2:Hello,1:Hi,2:World!]- in natural (alphabet) order
    // Print all unique words
    System.out.println(bag.uniqueSet());    // print [All!, Hello, Hi, World!]- in natural (alphabet) order


    // Print count occurrences of words
    System.out.println("Hello = " + bag.getCount("Hello"));    // print 2
    System.out.println("World = " + bag.getCount("World!"));    // print 2
    System.out.println("All = " + bag.getCount("All!"));    // print 1
    System.out.println("Hi = " + bag.getCount("Hi"));    // print 1
    System.out.println("Empty = " + bag.getCount("Empty"));    // print 0

    // Print count all words
    System.out.println(bag.size());    //print 6

    // Print count unique words
    System.out.println(bag.uniqueSet().size());    //print 4

2. Verwenden von TreeBag aus Eclipse (GC) :

    // Parse text to separate words
    String INPUT_TEXT = "Hello World! Hello All! Hi World!";
    // Create Multiset
    MutableSortedBag<String> bag =  TreeBag.newBag(Arrays.asList(INPUT_TEXT.split(" ")));

    // Print count words
    System.out.println(bag); // print [All!, Hello, Hello, Hi, World!, World!]- in natural order
    // Print all unique words
    System.out.println(bag.toSortedSet());    // print [All!, Hello, Hi, World!]- in natural order

    // Print count occurrences of words
    System.out.println("Hello = " + bag.occurrencesOf("Hello"));    // print 2
    System.out.println("World = " + bag.occurrencesOf("World!"));    // print 2
    System.out.println("All = " + bag.occurrencesOf("All!"));    // print 1
    System.out.println("Hi = " + bag.occurrencesOf("Hi"));    // print 1
    System.out.println("Empty = " + bag.occurrencesOf("Empty"));    // print 0

    // Print count all words
    System.out.println(bag.size());    //print 6

    // Print count unique words
    System.out.println(bag.toSet().size());    //print 4

3. Using LinkedHashMultiset aus Guava :

    // Parse text to separate words
    String INPUT_TEXT = "Hello World! Hello All! Hi World!";
    // Create Multiset
    Multiset<String> multiset = LinkedHashMultiset.create(Arrays.asList(INPUT_TEXT.split(" ")));

    // Print count words
    System.out.println(multiset); // print [Hello x 2, World! x 2, All!, Hi]- in predictable iteration order
    // Print all unique words
    System.out.println(multiset.elementSet());    // print [Hello, World!, All!, Hi] - in predictable iteration order

    // Print count occurrences of words
    System.out.println("Hello = " + multiset.count("Hello"));    // print 2
    System.out.println("World = " + multiset.count("World!"));    // print 2
    System.out.println("All = " + multiset.count("All!"));    // print 1
    System.out.println("Hi = " + multiset.count("Hi"));    // print 1
    System.out.println("Empty = " + multiset.count("Empty"));    // print 0

    // Print count all words
    System.out.println(multiset.size());    //print 6

    // Print count unique words
    System.out.println(multiset.elementSet().size());    //print 4

Weitere Beispiele finden Sie in meinen Github-Projekten

2

Ich würde definitiv eine TreeMap wählen:

  • TreeMap sortiert beim Einfügen automatisch neue Schlüssel, danach ist keine Sortierung erforderlich.
  • Wenn ein Schlüssel bereits vorhanden ist, hat er dieselbe Leistung wie eine HashMap.

Ein TreeSet verwendet intern eine TreeMap, also verwenden Sie TreeMap nicht direkt.

1
Chris

Abhängig von den Geschwindigkeitsanforderungen können Sie auch eine Trie verwenden. Es ist jedoch sinnlos, eine davon zu implementieren, wenn eine TreeMap schnell genug ist.

0
CAdaker

berücksichtigen Sie die Häufigkeit des Hinzufügens oder Löschens zur Datenstruktur. TreeMap wäre nicht ideal, wenn es hoch ist. Abgesehen von der Suche nach bestehenden Einträgen nLn wird auch häufig ein Rebalancing durchgeführt.

auf der anderen Seite sind Hash-Strukturen im Speicher etwas flamboyant (over allocates). Wenn Sie diese Kugel beißen können, wählen Sie die Hash-Struktur und sortieren Sie sie, wenn nötig.

0
G Kumar

Hier ist das Java-Beispiel zum Lesen einer Textdatei, zum Sortieren nach Schlüssel und dann nach Werten. abhängig von der Anzahl der Vorkommen eines Wortes in der Datei.

public class SortFileWords {

    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        ValueCompare vc = new ValueCompare(map);
        TreeMap<String, Integer> sorted_map = new TreeMap<String, Integer>(map);
        List<String> list = new ArrayList<>();
        Scanner sc;
        try {
            sc = new Scanner(new File("c:\\ReadMe1.txt"));
            while (sc.hasNext()) {
                list.add(sc.next());
            }
            sc.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        for (String s : list) {
            if (map.containsKey(s)) {
                map.put(s, map.get(s) + 1);
            } else
                map.put(s, 1);
        }

        System.out.println("Unsorted map: " + map);
        sorted_map.putAll(map);
        System.out.println("Sorted map on keys: " + sorted_map);

        TreeMap<String, Integer> sorted_value_map = new TreeMap<>(vc);
        sorted_value_map.putAll(map);
        System.out.println("Sorted map on values: " + sorted_value_map);
    }
}

class ValueCompare implements Comparator<String> {

    Map<String, Integer> map;

    public ValueCompare(Map<String, Integer> map) {
        this.map = map;
    }

    @Override
    public int compare(String s1, String s2) {
        if (map.get(s1) >= map.get(s2))
            return -1;
        else
            return 1;
    }
}
0
hardeep thakur