您将使用哪种数据结构:TreeMap或HashMap? (JAVA)

时间:2008-11-19 15:55:13

标签: java data-structures map hashmap treemap

描述| 一个Java程序,用于读取文本文件并按字母顺序打印每个唯一单词以及单词在文本中出现的次数。

程序应该声明一个Map<String, Integer>类型的变量来存储单词和相应的出现频率。但是哪种具体类型呢? TreeMap<String, Number>HashMap<String, Number>

输入应转换为小写。

单词不包含以下任何字符:\t\t\n]f.,!?:;\"()'

示例输出|

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

备注| 我知道,我已经在Perl中看到了大致两行代码的优雅解决方案。但是,我想在Java中看到它。

编辑:哦,是的,使用这些结构中的一个显示实现是有帮助的(在Java中)。

14 个答案:

答案 0 :(得分:59)

TreeMap对我来说似乎是明智的 - 仅仅是因为“按字母顺序”的要求。迭代时HashMap没有排序; TreeMap以自然键顺序迭代。

编辑:我认为康拉德的评论可能暗示“使用HashMap,然后排序”。这很好,因为尽管我们最初会进行N次迭代,但由于重复,我们最终会得到K <= N个密钥。我们不妨将昂贵的位(排序)保存到最后,当我们得到的密钥少于采取小的但非常量的命中时保持它的排序。

话虽如此,我现在仍然坚持我的答案:因为这是实现目标的最简单方式。我们并不是真的知道OP特别担心性能,但问题暗示他关注优雅和简洁。使用TreeMap使这个非常简短,这对我很有吸引力。我怀疑如果性能真的是一个问题,可能有一种比TreeMap或HashMap更好的攻击方式:)

答案 1 :(得分:18)

TreeMap胜过HashMap,因为TreeMap已经为你排序了。

但是,您可能需要考虑使用更合适的数据结构,即包。看到 Commons Collections - 以及TreeBag类:

这有一个很好的内部结构和API:

bag.add("big")
bag.add("small")
bag.add("big")
int count = bag.getCount("big")
编辑:Jon-HashMap回答了HashMap与TreeMap性能的问题,排序可能更快(尝试一下!),但TreeBag更容易。包袋也是如此。有一个HashBag和一个TreeBag。根据实现(使用可变整数),一个包应该胜过Integer的等效平面映射。唯一可以确定的方法是测试,就像任何性能问题一样。

答案 2 :(得分:11)

我看到很多人说“TreeMap查找需要O(n log n)”!!怎么会?

我不知道它是如何实现的,但在我脑海中需要O(log n)

这是因为树中的查找可以在O(log n)中完成。每次在其中插入项目时,都不会对整个树进行排序。这就是使用树的全部想法!

因此,回到最初的问题,比较的数字结果是:

HashMap方法: O(n + k log k)平均情况,最坏情况可能会大得多

TreeMap方法: O(k + n log k)最坏情况

其中n =文本中的单词数,k =文本中不同单词的数量。

答案 3 :(得分:3)

哈希地图应该快得多。您不应该根据您希望最终安排项目的方式选择容器;只需在最后对(字,频率)对列表进行排序。通常会有比文件中的单词更少的这类对,因此使用哈希映射的渐近(和真实)性能会更好。

答案 4 :(得分:2)

您无法将TreeMap<String,Number>分配给类型为Map<String,Integer>的变量。 DoubleLong等可以“放入”TreeMap<String,Number>。当我“获取”Map<String,Integer>中的值时,它必须是Integer

完全忽略任何i18n问题,内存限制和错误处理,这里是:

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

}

答案 5 :(得分:2)

“当一个密钥已经存在时,它具有与HashMap相同的性能。” - 这是完全错误的。 HashMap具有O(1)插入和TreeMap O(n log n)。至少需要n个n次检查才能确定它是否在表中!

答案 6 :(得分:2)

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

}

答案 7 :(得分:2)

这样,在我看来,最好使用HashBag中的Apache Commons Collections或来自HashMultisetGuava或来自HashBagEclipse Collections(形式上) GS Collections)或以下任何类:

@user

示例:

1。 Using SynchronizedSortedBag from Apache

    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>))
────────────────────────────────────────────────────────────────────────

2. Using TreeBag from Eclipse(GC)

    // 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

3。 Using LinkedHashMultiset from Guava

    // 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

More examples you can find in my github projects

答案 8 :(得分:1)

我肯定会选择一个TreeMap:

  • TreeMap会在插入时自动对新键进行排序,之后不需要排序。
  • 当密钥已存在时,它具有与HashMap相同的性能。

TreeSet内部使用TreeMap,所以为什么不直接使用TreeMap。

答案 9 :(得分:0)

根据速度要求,您还可以使用Trie。但是,如果TreeMap足够快,那么实现其中一个就没有意义了。

答案 10 :(得分:0)

考虑数据结构的添加或删除频率。如果它很高,TreeMap就不理想了。除了搜索现有的入境nLn之外,它还经常进行重新平衡。

另一方面,

哈希结构在内存上有点夸张(超过分配)。如果你可以咬那个子弹,那就去哈希结构并在需要时进行排序。

答案 11 :(得分:0)

以下是用于读取文本文件,基于键进行排序,然后根据值进行排序的java示例;取决于文件中单词的出现次数。

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

答案 12 :(得分:-2)

为什么不使用TreeSet

与TreeMap相同的排序概念,除了它是一个Set - 根据定义,它是“不包含重复元素的集合”。

从您的问题描述中,听起来好像您需要一个Set,我看不到您要映射的键和值。

  

此类实现由TreeMap实例支持的Set接口。此类保证有序集按升序元素顺序排序,根据元素的自然顺序排序(请参阅Comparable),或者根据设置创建时提供的比较器排序,具体取决于使用的构造函数。

答案 13 :(得分:-3)

基本上取决于要求。有时哈希映射有时候是树形图。但是哈希映射最好只使用它们对开销进行排序的一些约束。