Reputation: 2245
I have scenario where I would like to have a sorted map with a tuple as key:
var scheduledRunnables = new TreeMap<Tuple<Integer, String>, Runnable>(Comparator.comparing(Tuple::getKey));
When adding, I want to add a tuple:
scheduledRunnables.put(new Tuple<>(order, taskName), task);
When sorting, I want it to be sorted by the order integer, since that determines order of execution of the runnables. I do not necessarily know the value of the variable taskName
. When calling get
, I want to only supply the order integer, because of the same reason as previously stated. When calling put
, I want to take the entire pair into consideration. So something like this:
scheduledRunnables.put(new Tuple<>(1, "Some task"), () -> System.out.println("asdf"));
scheduledRunnables.get(1).run(); // Should output 'asdf'
scheduledRunnables.put(new Tuple<>(1, "Some task"), () -> System.out.println("qwerty"));
scheduledRunnables.get(1).run(); // Should output 'qwerty'
scheduledRunnables.remove(1).run(); // Should output 'qwerty' and remove from map
The Tuple
class is just a data holder that looks like this:
@Data
public class Tuple<K, V> {
private final K key;
private V value;
public Tuple(K key, V value) {
this.key = key;
this.value = value;
}
}
How would this be implemented? Is is possible without making a custom implementation of the SortedMap interface? Is there a better way to achieve what I am after?
Upvotes: 2
Views: 15620
Reputation: 6595
Another option is to use a String as a key, where String will be a concatenation of Integer + String (perhaps add a "_ " as a spacer in case String has some digits.) This way the order will be handled automatically, just implement similar mechaninsm to look up the values.
var scheduledRunnables = new TreeMap<String, Runnable>();
Then, if you need to get all values with "1" in the key, you can use
scheduledRunnables.keys()
and get those starting with "1".
Upvotes: 1
Reputation: 44200
Honestly, just maintain two maps.
Map<Integer, Runnable> idToRunnable;
Map<Integer, String> idToName;
Or use the tuple for the value rather than the key:
Map<Integer, Tuple<String, Runnable>>
Upvotes: 3
Reputation: 2283
When calling
get
, I want to only supply the order integer
That has some hidden assumptions that aren't being enforced by your code. If you're using a Tuple as your key, then getting an entry out of the map by just one part of that Tuple has the possibility of returning multiple entries. By way of example, consider this simplified code:
Map<Tuple<Integer, String>, String> map = new TreeMap<>();
map.put(new Tuple<>(1, "Foo"), "First");
map.put(new Tuple<>(1, "Bar"), "Second");
? = map.get(1); // What should this return?
That get()
, if you only cared about part of the Tuple, would be expected to return multiple values.
If you don't care about part of your Tuple, then you shouldn't be using Tuples as keys to your map, since keys are expected to be distinct, but parts of keys are not. Without knowing more about your use case, I'm not sure what the best option is for you, though.
Upvotes: 1
Reputation: 19926
You can just make you value
not relevant (its just a meta information). So its not needed for initializing the class (optional parameter) and not taken into account when using hashCode
or equals
:
public class Tuple<K extends Comparable<K>, V> implements Comparable<Tuple<K, V>> {
private final K key;
private V value;
public Tuple(K key) {
this.key = key;
}
public Tuple(K key, V value) {
this.key = key;
this.value = value;
}
public int hashCode() {
return key.hashCode();
}
public boolean equals(Object obj) {
return obj == this || (obj instanceof Tuple) && key.equals(((Tuple) obj).key);
}
public int compareTo(Tuple<K, V> other) {
return key.compareTo(other.key);
}
}
I've also made Tuple
comparable, which will just compare the keys, so you can create your TreeMap
without a custom Comparator
like this:
var scheduledRunnables = new TreeMap<Tuple<Integer, String>, Runnable>();
You then can call your get with:
Runnable runnable = scheduledRunnables.get(new Tuple<>(1));
Upvotes: 3