【问题标题】:Comparing keys in HashMap and Values比较 HashMap 和 Values 中的键
【发布时间】:2017-11-16 17:34:25
【问题描述】:

我有一个HashMap如下-

HashMap<String, Integer> BC = new HashMap<String, Integer>();

它存储为键-“令牌/标签”和值-“每个令牌/标签的频率”。

例子-

"the/at" 153
"that/cs" 45
"Ann/np" 3

我现在解析每个键并检查是否对同一个标记说“the”,它是否与多个标签相关联,然后取两者中最大的一个。

例子-

"the/at" 153
"the/det" 80

然后我拿钥匙-"the/at" 和价值-153

我为此编写的代码如下-

private HashMap<String, Integer> Unigram_Tagger = new HashMap<String, Integer>();

for(String curr_key: BC.keySet())
        {
            for(String next_key: BC.keySet())
            {
                if(curr_key.equals(next_key))
                    continue;
                else
                {
                    String[] split_key_curr_key = curr_key.split("/");
                    String[] split_key_next_key = next_key.split("/");

                    //out.println("CK- " + curr_key + ", NK- " + next_key);

                    if(split_key_curr_key[0].equals(split_key_next_key[0]))
                    {
                        int ck_v = 0, nk_v = 0;
                        ck_v = BC.get(curr_key);
                        nk_v = BC.get(next_key);

                        if(ck_v > nk_v)
                            Unigram_Tagger.put(curr_key, BC.get(curr_key));
                        else
                            Unigram_Tagger.put(next_key, BC.get(next_key));
                    }
                }
            }
        }

但是这段代码的计算时间太长了,因为原始的 HashMap 'BC' 有 68442 个条目,大约是它的平方 = 4684307364 次(加上更多)。

我的问题是——我可以使用更有效的方法完成相同的输出吗?

谢谢!

【问题讨论】:

    标签: java hashmap


    【解决方案1】:

    新建一个

    Map<String,Integer> highCount = new HashMap<>();
    

    这会将令牌映射到它们的最大计数。

    单次通过键。

    将每个键拆分为其组件标记。

    对于每个令牌,请查看 highMap。如果密钥不存在,则将其与计数相加。如果条目已经存在并且当前计数大于之前的最大值,则替换映射中的最大值。

    当您完成单次传递后,highCount 将包含所有唯一令牌以及每个令牌看到的最高计数。

    注意:此答案旨在为您提供一个开发完整解决方案的起点。关键概念是您创建并填充从令牌到某个“值”类型(不一定只是 Integer)的新映射,它为您提供所需的功能。值类型很可能是存储标签和计数的新自定义类。

    【讨论】:

    • 我在编码时遇到的问题是 - 我可以使用 String[] curr_k = key.split("/");但是我将再次需要第二个“for”循环来遍历“highCount”,因为“token”可以相同,但“tag”会不同。
    • 您还需要存储每个标记的最高值是哪个标签值,对吧?
    • 是的。我的回答旨在作为 OP 构建解决方案的起点,关键是创建一个新地图来保存令牌及其计数。地图的“值”类型可能需要是一个自定义类来保存计数和对标签的引用。我已经更新了我的答案。
    【解决方案2】:

    当前方法中最慢的部分是由于键的成对比较。首先,定义一个Tuple 类:

    public class Tuple<X, Y> { 
      public final X x; 
      public final Y y; 
      public Tuple(X x, Y y) { 
        this.x = x; 
        this.y = y; 
      } 
    } 
    

    因此,您可以尝试以下算法:

    1. 初始化一个新的HashMap&lt;String, Tuple&lt;String, Integer&gt;&gt; result
    2. 给定来自旧地图的输入对(key, value),其中key = "a/b",检查是否result.keySet().contains(a)result.keySet().contains(b)
    3. 如果ab 都不存在,则result.put(a, new Tuple&lt;String, Integer&gt;(b, value)result.put(b, new Tuple&lt;String, Integer&gt;(a, value))
    4. 如果存在a,则比较valuev = result.get(a)。如果value &gt; v,从result 中删除ab 并执行步骤3。对b 执行相同操作。否则,获取下一个键值对。

    在您遍历旧的哈希映射并插入所有内容后,您可以通过转换 result 中的键值来轻松重建所需的输出。

    【讨论】:

      【解决方案3】:

      算法的基本思路:

      1. 你应该得到 HashMap 的 entrySet() 并将其转换为 List:

        ArrayList<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
        
      2. 现在您应该按字母顺序按键对列表进行排序。我们这样做是因为 HashMap 没有顺序,因此您可以预期相应的键可能相距甚远。但是通过对它们进行排序,所有相关的键都直接相邻。

        Collections.sort(list, Comparator.comparing(e -> e.getKey()));
        

        由于按字母顺序排序,条目“the/at”和“the/det”将彼此相邻。

      3. 现在您可以在记住最佳项目的同时遍历整个列表,直到找到更好的项目或找到具有不同前缀的第一个项目(例如“the”)。

        ArrayList<Map.Entry<String, Integer>> bestList = new ArrayList<>();
        // The first entry of the list is considered the currently best item for it's group
        Map.Entry<String, Integer> currentBest = best.get(0);
        String key = currentBest.getKey();
        String currentPrefix = key.substring(0, key.indexOf('/'));
        
        for (int i=1; i<list.size(); i++) {
            // The item we compare the current best with
            Map.Entry<String, Integer> next = list.get(i);
            String nkey = next.getKey();
            String nextPrefix = nkey.substring(0, nkey.indexOf('/'));
        
            // If both items have the same prefix, then we want to keep the best one 
            // as the current best item
            if (currentPrefix.equals(nextPrefix)) {
                if (currentBest.getValue() < next.getValue()) {
                    currentBest = next;
                }
        
            // If the prefix is different we add the current best to the best list and 
            // consider the current item the best one for the next group
            } else {
                bestList.add(currentBest);
                currentBest = next;
                currentPrefix = nextPrefix;
            }
        }
        // The last one must be added here, or we would forget it
        bestList.add(currentBest);
        
      4. 现在您应该有一个 Map.Entry 对象列表,代表所需的条目。复杂度应为 n(log n) 并受排序算法的限制,而对项目进行分组/收集的复杂度为 n。

      【讨论】:

      • 当我测试上面的代码时,它给了我错误- java.lang.StringIndexOutOfBoundsException: String index out of range: -1 和给出错误的行- String currentPrefix = key.substring( 0, key.indexOf('/'));
      • 如果不是所有键都包含斜杠字符/,就会发生这种情况。在这个简单的解决方案中,我没有对此进行检查。您可以将currentPrefixnextPrefix 的行更改为:String currentPrefix = key.split("/")[0];String nextPrefix = nkey.split("/")[0];
      【解决方案4】:
      import java.util.Comparator;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      import java.util.Map.Entry;
      import java.util.TreeMap;
      import java.util.stream.Collectors;
      
      public class Point {
      
          public static void main(String[] args) {
              HashMap<String, Integer> BC = new HashMap<>();
              //some random values
              BC.put("the/at",5);
              BC.put("Ann/npe",6);
              BC.put("the/atx",7);
              BC.put("that/cs",8);
              BC.put("the/aty",9);
              BC.put("Ann/np",1);
              BC.put("Ann/npq",2);
              BC.put("the/atz",3);
              BC.put("Ann/npz",4);
              BC.put("the/atq",0);
              BC.put("the/atw",12);
              BC.put("that/cs",14);
              BC.put("that/cs1",16);
              BC.put("the/at1",18);
              BC.put("the/at2",100);
              BC.put("the/at3",123);
              BC.put("that/det",153);  
              BC.put("xyx",123);
              BC.put("xyx/w",2);  
              System.out.println("\nUnsorted Map......");
              printMap(BC); 
      
              System.out.println("\nSorted Map......By Key"); 
              //sort original map using TreeMap, it will sort the Map by keys automatically.
              Map<String, Integer> sortedBC = new TreeMap<>(BC);
              printMap(sortedBC);
              //  find all distinct prefixes by spliting the keys at "/"
              List<String> uniquePrefixes = sortedBC.keySet().stream().map(i->i.split("/")[0]).distinct().collect(Collectors.toList());
              System.out.println("\nuniquePrefixes: "+uniquePrefixes);        
      
              TreeMap<String,Integer> mapOfMaxValues = new TreeMap<>();
              // for each prefix from the list above filter the entries from the sorted map 
              // having keys starting with this prefix 
              //and sort them by value in descending order and get the first which will have the highst value
              uniquePrefixes.stream().forEach(i->{ 
                      Entry <String,Integer> e = 
                      sortedBC.entrySet().stream().filter(j->j.getKey().startsWith(i))
                      .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).findFirst().get();
      
                      mapOfMaxValues.put(e.getKey(), e.getValue());
                  });
      
              System.out.println("\nmapOfMaxValues...\n");
              printMap(mapOfMaxValues);  
          }
          //pretty print a map
          public static <K, V> void printMap(Map<K, V> map) {
              map.entrySet().stream().forEach((entry) -> {
                  System.out.println("Key : " + entry.getKey()
                          + " Value : " + entry.getValue());
              });
          }
      }
      
      // note: only tested with random values provided in the code 
      // behavior for large maps untested
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-06
        • 2016-03-31
        • 2015-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-25
        相关资源
        最近更新 更多