【问题标题】:How can I split a delimited list and recombine the values into a hash map?如何拆分分隔列表并将值重新组合成哈希映射?
【发布时间】:2018-02-03 14:21:27
【问题描述】:

我一直在编写以下内容以尝试更好地理解哈希映射。我有一个由分隔符 | 分隔的原始项目列表。目标是确保最后我有一个 HashMap,其中每个字母都有一个键,即出现在 | 之前的 a 并附加到每个字母的列表中,其中包含相关的“日志”,或者出现在| 之后,即 a = [asdf a2, asdf a1]。为了清楚起见,我将 a1/a2 放在列表的“日志”部分。假设当真正执行此代码时,这些提示将不存在。我只想根据初始的原始分隔列表将所有“日志”分组到相应的键中。

计划:

public static void main(String[] args) {

        List<String> items = new ArrayList<>();

        // Set up raw data
        items.add("a|asdf a2");
        items.add("b|asdf b1");
        items.add("c|asdf c1");
        items.add("c|asdf c2");
        items.add("c|asdf c3");
        items.add("d|asdf d1");
        items.add("a|asdf a1");
        items.add("e|asdf e1");
        items.add("e|asdf e2");
        items.add("e|asdf e3");
        items.add("e|asdf e4");

        // Display raw data
        System.out.println("Raw List: " + items);

        // Create a hash map
        HashMap<String, List<String>> customerHashMap = new HashMap<>();

        // Create new lists. One for customers and one for logs
        List<String> customerList = new ArrayList<>();
        List<String> logList = new ArrayList<>();
        for (String item : items) {
            String customer = item.split("\\|", 2)[0];
            customerList.add(customer);

            String log = item.substring(item.indexOf("|") + 1);
            logList.add(log);

            // Add to hash map
            customerHashMap.put(customer, logList);
        }

        // Display lists
        System.out.println("Customer List: " + customerList);
        System.out.println("Log List: " + logList);
        System.out.println("Hashmap: " + customerHashMap);

        // Print out of the final hash map. Customer a should only have "a" logs, customer b with "b", etc.
        System.out.println("");
        Iterator it = customerHashMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            System.out.println(pair.getKey() + " = " + pair.getValue());
            it.remove();
        }

    }

输出:

a = [asdf a2, asdf b1, asdf c1, asdf c2, asdf c3, asdf d1, asdf a1, asdf e1, asdf e2, asdf e3, asdf e4]
b = [asdf a2, asdf b1, asdf c1, asdf c2, asdf c3, asdf d1, asdf a1, asdf e1, asdf e2, asdf e3, asdf e4]
c = [asdf a2, asdf b1, asdf c1, asdf c2, asdf c3, asdf d1, asdf a1, asdf e1, asdf e2, asdf e3, asdf e4]
d = [asdf a2, asdf b1, asdf c1, asdf c2, asdf c3, asdf d1, asdf a1, asdf e1, asdf e2, asdf e3, asdf e4]
e = [asdf a2, asdf b1, asdf c1, asdf c2, asdf c3, asdf d1, asdf a1, asdf e1, asdf e2, asdf e3, asdf e4]\

所需的输出:

a = [asdf a2, asdf a1]
b = [asdf b1]
c = [asdf c1, asdf c2, asdf c3]
d = [asdf d1]
e = [asdf e1, asdf e2, asdf e3, asdf e4]

我怎样才能在最后实现这个期望的输出?

【问题讨论】:

  • 您希望为每个客户创建一个单独的日志列表,但您正在创建一个日志列表。这怎么可能?
  • 考虑使用Guava ListMultiMap
  • @JBNizet 有道理...

标签: java list arraylist hashmap hashset


【解决方案1】:
List<String> logList = new ArrayList<>();

在循环之前声明,并且永远不会重新分配此变量。
您将其用作与循环中Map 的每个键关联的值。
所以地图的所有键都指向同一个ArrayList 对象。

您必须为每个不同的键创建一个新的ArrayList 并将其与映射中的键 (put(key,value)) 相关联。

这可能会给:

    for (String item : items) {
        String customer = item.split("\\|", 2)[0];
        customerList.add(customer);

        String log = item.substring(item.indexOf("|") + 1);
        List<String> logList = customerHashMap.get(customer);
        if (logList == null){
          logList = new ArrayList<>();
          customerHashMap.put(customer, logList);
        }           
        logList.add(log);
    }

或者正如 JB Nizet 建议的那样,使用 Map.computeIfAbsent()(从 Java 8 开始),这样您既可以显式编写不存在检查,也可以声明一个中间局部变量来引用当前的 ArrayList 对象。

    for (String item : items) {
        String customer = item.split("\\|", 2)[0];
        customerList.add(customer);

        String log = item.substring(item.indexOf("|") + 1);
        customerHashMap.computeIfAbsent(customer, c -> new ArrayList<>()).add(log);
    }

【讨论】:

  • 或者您可以使用为该频繁用例设计的快捷方法:customerHashMap.computeIfAbsent(customer, c -&gt; new ArrayList&lt;&gt;()).add(log);
  • 优化思路:无需重新计算log;它已经在item.split("\\|", 2)[1]
  • @DodgyCodeException 你是对的。但准确的说,已经在item.split("\\|", 2)
【解决方案2】:

您可以使用 Java 8 的新功能来满足您的要求。 通过这行简单的代码,您就可以实现您的要求:

items.stream().collect(Collectors.groupingBy(s -> s.charAt(0), Collectors.mapping(s -> s.substring(2), Collectors.toSet()))).forEach((k,v)-> {
        System.out.println(k + " = " + v);
    });

forEach 函数的结尾是打印结果,但是代码结果是一个带有你想要的结果的 Map:

Map<Character, Set<String>> result = items.stream().collect(Collectors.groupingBy(s -> s.charAt(0), Collectors.mapping(s -> s.substring(2), Collectors.toSet())));

注意:如果元素可以重复,请使用 Collectors.toList()

【讨论】:

  • 您没有将最后一个拆分为元素
  • @Schidu Luca 原来的项目List被拆分了,不需要拆分:)
猜你喜欢
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 2017-09-03
相关资源
最近更新 更多