I am doing dynamic programming in java and I came across this problem where the function takes three parameters, two of those are integers, and the third is an array of integers. I know that caching the value of a 1- or 2-parameter function can be implemented using a 1d/2d array. I am thinking of may be using a HashMap but I am trying to look for a nicer way to have those three indices bundled together as a key to the hashmap
3 Answers
Create a new class which holds all parameters and add it to the map.
Make sure to also implement hashCode() & equals()
class MemoKey {
Integer a;
Integer b;
Integer[] array;
public MemoKey(Integer a,Integer b, Integer[] array) {
this.a = a;
this.b = b;
this.array = array;
}
@Override
public int hashCode() {
// implement
}
@Override
public boolean equals(Object obj) {
// implement
}
}
Then put this class in the map with your result Object.
HashMap map = new HashMap<MemoKey, Object>();
Object result = map.get(memoKey);
if (result == null)
map.put(memoKey, calcResult());
Comments
Here's an example, using Apache Commons Collections:
public static void main(String[] args) {
MultiKeyMap multiKeyMap = new MultiKeyMap();
int[] array1 = new int[10];
int[] array2 = new int[10];
multiKeyMap.put(13,52,array1, "First");
multiKeyMap.put(81,22,array2, "Second");
System.out.println(multiKeyMap.get(81,22,array2));
}
3 Comments
Create immutable class accepting two generics and an integer array as a constant type (you can change it to if you know that other numeric types will be used):
public final class CustomKey<F,S> {
private final F firstValue;
private final S secondValue;
private final int[] thirdValue;
public CustomKey(final F firstValue,
final S secondValue,
final Integer[] thirdValue){
this.firstValue = firstValue;
this.secondValue = secondValue;
this.thirdValue = unboxingArray(thirdValue);
}
public S getSecondValue() {
return secondValue;
}
public F getFirstValue() {
return firstValue;
}
public int[] getThirdValue() {
return thirdValue;
}
private static int[] unboxingArray(final Integer[] array){
final int[] result = new int[array.length];
IntStream.range(0,array.length)
.forEach(index -> result[index] = array[index]);
return result;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final CustomKey<?, ?> customKey = (CustomKey<?, ?>) o;
return getFirstValue().equals(customKey.getFirstValue())
&& getSecondValue().equals(customKey.getSecondValue())
&& Arrays.equals(getThirdValue(), customKey.getThirdValue());
}
@Override
public int hashCode() {
int result = getFirstValue().hashCode();
result = 31 * result + getSecondValue().hashCode();
result = 31 * result + Arrays.hashCode(getThirdValue());
return result;
}
}
Unboxing of Integer array is necessary if you want to be able to compare integers in the array. If you will implement other type, do instanceOf check before unboxing.
Then, load your map as you would with any other key:
public static void main(String[]args){
//Assume your Value is a String which does not really matter
Map<CustomKey<Integer,Integer>,String> customKeyMap =
new HashMap<>();
customKeyMap.put(new CustomKey<>(1,2,new Integer[]{1,2,3}),"First Value");
customKeyMap.put(new CustomKey<>(1,3,new Integer[]{1,2,3}),"Second Value");
//Can use anonymous instance since equals is implemented
//Expect Second Value
System.out.println(customKeyMap.get(new CustomKey<>(1,3,new Integer[]{1,2,3})));
}
Keyclass that can contains those 3 values?