Reputation: 63
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?
Upvotes: 3
Views: 1711
Reputation: 14338
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
Upvotes: 4
Reputation: 37645
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.
Upvotes: 3
Reputation: 1844
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.
Upvotes: 3