I have a LinkedHashMap and I am trying to split its keys and values and refer to specific key in the keyset or value in valueset. For ex. Lets say I have the folowing LinkedHashMap:
4 | 2
3 | 1
I want a function to return 1, which is a value at index 1 in the set with values or return 2 for index = 0. And with another function I want to get 3 as value with index 1 in key values. So basically from the LinkedHashMap create an Array with only values/ keys and then look for some given place in this array. My code looks as follow:
public static Integer getLHMValue(Map<Integer, Long> lhm, int index) {
Set<Map.Entry<Integer, Long>> valueSet = lhm.entrySet();
Integer[] valueArray = valueSet.toArray(new Integer[valueSet.size()]);
Integer value = valueArray[index];
return value;
}
public static Integer getLHMKey(Map<Integer, Long> lhm, int index) {
Set<Integer> keySet = lhm.keySet();
Integer[] keyArray = keySet.toArray(new Integer[keySet.size()]);
Integer key = keyArray[index];
return key;
}
Though, I get java.lang.ArrayStoreException: java.util.LinkedHashMap$Entry in: Integer[] keyArray = valueSet.toArray(new Integer[valueSet.size()]);. Any ideas?
CodePudding user response:
The problem is that the entrySet would be needed to first have an array of Map.Entry
s. Keeping such an array for both methods would be faster.
List<Map.Entry<Integer, Long>> entries = new ArrayList<>(lhm.entrySet());
public static long getLHMValue(Map<Integer, Long> lhm, int index) {
return entries.get(index).getValue();
}
public static int getLHMKey(Map<Integer, Long> lhm, int index) {
return entries.get(index).getKey();
}
There is no efficient solution if you do it directly, and so it is not advisable to offer these functions to access key and value.
public static Long getLHMValue(Map<Integer, Long> lhm, int index) {
return lhm.entrySet().stream()
.map(Map.Entry<Integer, Long>::getValue)
.skip(index)
.findFirst()
.orElseThrow(IndexOutOfBoundsException::new);
}
public static Integer getLHMKey(Map<Integer, Long> lhm, int index) {
return lhm.entrySet().stream()
.map(Map.Entry<Integer, Long>::getKey)
.skip(index)
.findFirst()
.orElseThrow(IndexOutOfBoundsException::new);
}
CodePudding user response:
In getLHMValue
the problem is your array creation:
Integer[] keyArray = valueSet.toArray(new Integer[valueSet.size()]);
because valueSet
is Set<Map.Entry<Integer, Long>>
.
Modify your initialisation of valueSet
to
Collection<Long> valueSet = lhm.values();
But now you get the problem that Collection
does not ensure any order.
Btw: your values are Long
so you also should return Long
CodePudding user response:
Update
Firstly, it's highly advisable to favor Collections over Arrays, because collections can offer you behavior and arrays have a type-safety pitfall due to their covariant behavior (refer to "Effective Java" by Joshua Bloch, item "Prefer lists to arrays").
Since the map is always the same (clarification provided by OP in the comments) you can declare a list as a stitic final
variable, dump map entries into it and access this list from your methods.
That would allow to avoid performing unnecessary iteration each method call which costs O(n) time.
public static final Map<Integer, Long> LHM = // initializing the map
public static final List<Map.Entry<Integer, Long>> ENTRIES = new ArrayList<>(LHM.entrySet());
With that change your methods would work in constant time O(1) instead of linear.
public static Long getLHMValue(Map<Integer, Long> lhm, int index) {
return ENTRIES.get(index).getValue();
}
public static Integer getLHMKey(Map<Integer, Long> lhm, int index) {
return ENTRIES.get(index).getKey();
}
You can introduce a variable that would represent the current index.
Then iterate over the entries until the target index is reached.
Return the key/value from the target entry
public static Long getLHMValue(Map<Integer, Long> lhm, int index) {
if (index < 0 || index >= lhm.size()) {
throw new IllegalArgumentException();
}
int i = 0;
Map.Entry<Integer, Long> result = null;
for (Map.Entry<Integer, Long> entry: lhm.entrySet()) {
if (i == index) result = entry;
i ;
}
return result.getValue();
}
Alternativelly, you can use Java 11 method Collection.toArray(IntFunction<T[]>)
or it's sibling Collection.toArray(T[])
available with earlier versions.
public static Long getLHMValue(Map<Integer, Long> lhm, int index) {
return lhm.values().toArray(Long[]::new)[index]; // or lhm.values().toArray(new Long[0])[index];
}