Reputation: 1176
Is it possible to iterate over a certain range of keys from a HashMap
?
My HashMap contains key-value pairs where the key denotes a certainr row-column in Excel (e.g. "BM"
or "AT"
) and the value is the value in this cell.
For example, my table import is:
startH = {
BQ=2019-11-04,
BU=2019-12-02,
BZ=2020-01-06,
CD=2020-02-03,
CH=2020-03-02,
CM=2020-04-06
}
endH = {
BT=2019-11-25,
BY=2019-12-30,
CC=2020-01-27,
CG=2020-02-24,
CL=2020-03-30,
CP=2020-04-27
}
I need to iterate over those two hashmap using a key-range in order to extract the data in the correct order. For example from "BQ"
to "BT"
.
Upvotes: 3
Views: 1388
Reputation: 25903
Is it possible to iterate over hashmap but using its index?
No.
A HashMap
has no indices. Depending on the underlying implementation it would also be impossible. Java HashMap
s are not necessarily represented by a hashing-table. It can switch over to a red-black tree and they do not provide direct access at all. So no, not possible.
There is another fundamental flaw in this approach. HashMap
does not maintain any order. Iterating it yields random orders that can change each time you start the program. But for this approach you would need insertion order. Fortunately LinkedHashMap
does this. It still does not provide index-based access though.
But, you actually do not even want index based access. You want to retrieve a certain key-range, for example from "BA"
to "BM"
. A good approach that works with HashMap
would be to generate your key-range and simply using Map#get
to retrieve the data:
char row = 'B';
char columnStart = 'A';
char columnEnd = 'M';
for (char column = columnStart; columnStart <= columnEnd; column++) {
String key = Chararcter.toString(row) + column;
String data = map.get(key);
...
}
You might need to fine-tune it a bit if you need proper edge case handling, like wrapping around the alphabet (use 'A' + (column % alphabetSize)
) and maybe it needs some char
to int
casting and vice versa for the additions, did not test it.
There is actually a variant of map that offers pretty much what you want out of the box. But at higher cost of performance, compared to a simple HashMap
. The interface is called NavigableMap
. The class TreeMap
is a good implementation. The problem is that it requires an explicit order. The good thing though is that you actually want String
s natural order, which is lexicographical.
So you can simply use it with your existing data and then use the method NavigableMap#subMap
:
NavigableMap<String, String> map = new TreeMap<>(...);
String startKey = "BA";
String endKey = "BM";
Map<String, String> subMap = map.subMap(startKey, endKey);
for (Entry<String, String> entry : subMap.entrySet()) {
...
}
If you have to do those kind of requests more than once, this will definitely pay off and it is the perfect data-structure for this use-case.
As explained before, it is also possible (although not as efficient) to instead have a LinkedHashMap
(to maintain insertion order) and then simply iterate over the key range. This has some major drawbacks though, for example it first needs to locate the start of the range by fully iterating to there. And it relies on the fact that you inserted them correctly.
LinkedHashMap<String, String> map = ...
String startKey = "BA";
String endKey = "BM";
boolean isInRange = false;
for (Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
if (!isInRange) {
if (key.equals(startKey)) {
isInRange = true;
} else {
continue;
}
}
...
if (key.equals(endKey)) {
break;
}
}
Upvotes: 3
Reputation: 16392
I don't believe you can. The algorithm you propose assumes that the keys of a HashMap are ordered and they are not. Order of keys is not guaranteed, only the associations themselves are guaranteed.
You might be able to change the structure of your data to something like this:
ranges = {
BQ=BT,
BU=BY,
....
}
Then the iteration over the HashMap keys (start cells) would easily find the matching end cells.
Upvotes: 0
Reputation: 4997
// rangeLower and rangeUpper can be arguments
int i = 0;
for (Object mapKey : map.keySet()) {
if (i < rangeLower || i > rangeUpper) {
i++;
continue;
}
// Do something with mapKey
}
The above code iterates by getting keyset and explicitly maintaining index and incrementing it in each loop. Another option is to use LinkedHashMap
, which maintains a doubly linked list for maintaining insertion order.
Upvotes: 0