简体   繁体   中英

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

I have a HashMap of ArrayLists as follows:

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

l , k and n take their values based on several loops and hence their values change depending on the parameters.

Under these circumstances, I am wanting to know for a given value of k, how the minimum and maximum values of the elements can be found in their relevant ArrayLists. (Please note that the length or ArrayLists is also dependent on the parameters)

For instance, let's say that I am wanting to know the minimum and maximum values within the ArrayList for k=3 . Then what I am looking for would be all the ArrayLists that have the key ("T_"+l+"_"+3+"_"+n) for every value of l and n . The problem here is that there is no way I can predict the values of l and n because they are totally dependent on the code. Another inconvenient thing is that I am wanting to get the minimum and maximum values out of the loops where l and n get their values, hence using these variables directly isn't feasible.

What would be an efficient way to get Java to call every value of l and n and fetch the values in the ArrayList in order to find the minimum and maximum of these values?

If you absolutely have to deal with such "smart keys", for any kind of processing based on its parts you first need functions to extract values of those parts:

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

These functions when applied to a key return k , l or n , respectively (if one knows more elegant way to do such, please comment or edit).

To be as more effective as possible (iterate not over entire map, but only over its part), suggest to switch from HashMap to any implementation of SortedMap with ordering based on values stored in a smart key:

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

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

Such you get a map where entries will be first sorted by k , then by l and finally by n . Now it is possible to get all lists mapped to a given k using:

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

To get max and min values around items of subMap , take the stream of its values, convert it to DoubleStream and use its .summaryStatistics() as follows:

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

The final part is to check whether values exist:

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

I'll simplify this a bit. Suppose you have a key that depends on an Integer k and a String s . It might seem a good idea to use a

Map<String, Object>

where the keys are k + " " + s (or something similar).

This is a terrible idea because, as you have realised, you have to iterate over the entire map and use String.split in order to find entries for a particular k value. This is extremely inefficient.

One common solution is to use a Map<Integer, Map<String, Object>> instead. You can get the object associated to k = 3, s = "foo" by doing map.get(3).get("foo") . You can also get all objects associated to 3 by doing map.get(3).values() .

The downside to this approach is that it is a bit cumbersome to add to the map. In Java 8 you can do

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

Google Guava's Table interface takes the pain out of using a data structure like this.

In Java 8 you could use DoubleSummaryStatistics and do something like this:

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 to keep only the entries you need; flatMapToDouble to merge your lists; and summaryStatistics to get both the minimum and maximum.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM