使用半已知键在 ArrayLists 的 HashMap 中查找最大值和最小值 - Java

Finding the maximum and minimum values in a HashMap of ArrayLists with semi-known key - Java

我有一个 ArrayLists 的 HashMap 如下:

HashMap<String, ArrayList<Double>> Flkn = new HashMap<String, ArrayList<Double>>();
Flkn.put("T_"+l+"_"+k+"_"+n, new ArrayList());

lkn 根据几个循环获取它们的值,因此它们的值会根据参数而变化。

在这种情况下,我想知道对于给定的 k 值,如何在相关的 ArrayList 中找到元素的最小值和最大值。 (请注意,长度或 ArrayLists 也取决于参数)

例如,假设我想知道 k=3 的 ArrayList 中的最小值和最大值。那么我正在寻找的是所有 ln 的每个值都具有键 ("T_"+l+"_"+3+"_"+n) 的 ArrayList。这里的问题是我无法预测 ln 的值,因为它们完全依赖于代码。另一个不方便的事情是我想从 ln 获取它们的值的循环中获取最小值和最大值,因此直接使用这些变量是不可行的。

什么是让 Java 调用 ln 的每个值并获取 ArrayList 中的值以找到的最小值和最大值的有效方法这些值?

我会稍微简化一下。假设您有一个密钥依赖于 Integer kString s。使用

似乎是个好主意
Map<String, Object>

键是 k + " " + s(或类似的东西)。

这是一个糟糕的想法,因为正如您已经意识到的那样,您必须遍历 整个 映射并使用 String.split 才能找到特定的条目k 值。这是非常低效的。

一种常见的解决方案是改用 Map<Integer, Map<String, Object>>。您可以通过 map.get(3).get("foo") 获取关联到 k = 3, s = "foo" 的对象。您还可以通过执行 map.get(3).values().

获取与 3 关联的所有对象

这种方法的缺点是添加到地图上有点麻烦。在 Java 8 你可以做到

map.computeIfAbsent(3, k -> new HashMap<String, Object>()).put("foo", "bar");

Google Guava's Table 接口消除了使用这样的数据结构的痛苦。

在 Java 8 中,您可以使用 DoubleSummaryStatistics 并执行如下操作:

final DoubleSummaryStatistics stats =
  Flkn.entrySet().stream().filter(e -> e.getKey().matches("T_[0-9]+_" + k + "_[0-9]+"))
                          .flatMapToDouble(e -> e.getValue().stream().mapToDouble(Double::doubleValue))
                          .summaryStatistics();
System.out.println(stats.getMax());
System.out.println(stats.getMin());

filter 只保留您需要的条目; flatMapToDouble 合并您的列表;和 summaryStatistics 以获得最小值和最大值。

如果您绝对必须处理这样的问题 "smart keys",对于基于其部分的任何类型的处理,您首先需要函数来提取这些部分的值:

final static Function<String, Integer> EXTRACT_K = s -> Integer.parseInt(s.replaceAll("T_\d+_(\d+)_\d+", ""));
final static Function<String, Integer> EXTRACT_L = s -> Integer.parseInt(s.replaceAll("T_(\d+)_\d+_\d+", ""));
final static Function<String, Integer> EXTRACT_N = s -> Integer.parseInt(s.replaceAll("T_\d+_(\d+)_\d+", ""));

这些函数分别应用于键 return kln(如果有人知道更优雅的方法,请发表评论或编辑)。

为了尽可能更有效(不是遍历整个地图,而是只遍历它的一部分),建议从 HashMap 切换到 SortedMap 的任何实现,并根据存储的值进行排序在智能钥匙中:

final static Comparator<String> CMP 
       = Comparator.comparing(EXTRACT_K)
                   .thenComparing(EXTRACT_L)
                   .thenComparing(EXTRACT_N);

SortedMap<String, List<Double>> map = new TreeMap<>(CMP);

这样你会得到一张地图,其中条目将首先按 k 排序,然后按 l,最后按 n 排序。现在可以使用以下方法将所有列表映射到给定的 k

int k = 1;
Collection<List<Double>> lists 
      = map.subMap(String.format("T_0_%s_0", k), String.format("T_0_%s_0", k + 1)).values();

要获取 subMap 项目的最大值和最小值,获取其值流,将其转换为 DoubleStream and use its .summaryStatistics(),如下所示:

DoubleSummaryStatistics s  
      = subMap.values().stream()
              .flatMapToDouble(vs -> vs.stream().mapToDouble(Double::doubleValue))
              .summaryStatistics();

最后一部分是检查值是否存在:

if (s.getCount() > 0) {
    max = s.getMax();
    min = s.getMin();
} else 
    // no values exist for a given k, thus max and min are undefined