我想知道是否存在任何Map<K, V>
实现,其中包含2个或更多枚举的枚举。
我将提供一个简化的例子。
说我有两个枚举DiceEyes
和CardSuit
:
public enum DiceEyes {
ONE, TWO, THREE, FOUR, FIVE, SIX
}
public enum CardSuit {
HEARTS, DIAMONDS, SPADES, CLUBS
}
现在我有6 * 4 = 24张图片,根据用户在GUI中选择的内容,我想从中显示一张图片 - 他选择了DiceEyes
和CardSuit
。
我无法控制图像的文件名。因此,通过命名图像<DiceType>-<CardSuit>-.png
或任何其他方式,我都无法聪明。因此,我需要将DiceType
和CardSuit
的组合映射到文件名。
我已经考虑过在[{1}} EnumMap
EnumMap
上运行的解决方案:
EnumMap<DiceType, EnumMap<CardSuit, String>> ... = new ...;
但这看起来过于复杂。然后,同事会认为DiceType
比CardSuit
更重要或更不重要,查看类型的顺序。
为此目的介绍一个包装类对我来说似乎有些过分。当我必须在Map
中使用它时,我需要实现equals()
和hashCode()
,为此目的看起来太多了。有了枚举,你已经保证了对象的平等性,所以我想继续使用枚举。
如果不引入特定的课程或过多的开销,如何实现这一目标?
答案 0 :(得分:4)
我会创建一个Pair<A, B>
类,然后您可以使用地图:
Map<Pair<DiceEyes, CardSuit>, String> = new HashMap<>();
答案 1 :(得分:3)
我已经知道实现了一个ArrayKey
对象,它允许我在地图等中使用数组作为键。
public class ArrayKey {
private Object[] array;
public ArrayKey(Object... array) {
this.array = array;
}
public boolean equals(Object other) { ... }
public int hashCode() { ...}
public Object[] getArray() { return array; }
}
然后你可以这样做:
Map<ArrayKey, String> map = new HashMap();
map.put(new ArrayKey(DiceEyes.ONE,CardSuit.HEARTS), "/path/to/file.jpg");
map.get(new ArrayKey(DiceEyes.ONE,CardSuit.HEARTS));
当然,为方便起见,可以扩展为创建一个ArrayKeyMap
public class ArrayKeyMap<T> {
private Map<ArrayKey, T> map = new HashMap();
public T put(Object... key, T value) {
return map.put(new ArrayKey(key), value);
}
public T get(Object... key) {
return map.get(new ArrayKey(key));
}
}
答案 2 :(得分:1)
使用Enum.name()方法创建字符串作为键怎么样?
String key=DiceEyes.ONE.name()+CardSuit.HEARTS.name(); //"ONEHEARTS".
答案 3 :(得分:1)
不幸的是,这种灵活性是不可能的。地图被定义为<K, V>
,基本就是那个。您提供的唯一方法是:创建关键对象或使用地图地图。 (或者我想你也可以编写自己的Map实现。)
由于你使用的是enum,所以制作一个包装器对象实际上很方便,但是我建议稍微改一下它。您可以使其不可变,并且每对只有一个实例。这将避免对象创建。如果需要,您可以覆盖equals
,但由于每对中只有一个,因此身份就足够了。您可以使用==
来比较它们,这也意味着您可以使用IdentityHashMap。
public final class DiceCardPair {
private final DiceEyes eyes;
private final CardSuit suit;
private DiceCardPair(
final DiceEyes eyes,
final CardSuit suit
) {
this.eyes = eyes;
this.suit = suit;
}
@Override
public int hashCode() {
// very compact hash code
// make shift bigger if more constants
return eyes.ordinal() | suit.ordinal() << 3;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof DiceCardPair)) {
return false;
}
DiceCardPair pair = (DiceCardPair)obj;
return eyes == pair.eyes && suit == pair.suit;
}
private static final DiceCardPair[][] values;
static {
DiceEyes[] eyes = DiceEyes.values();
CardSuit[] suits = CardSuit.values();
values = new DiceCardPair[eyes.length][suits.length];
for(int i = 0; i < values.length; i++) {
for(int k = 0; k < values[i].length; k++) {
values[i][k] = new DiceCardPair(
eyes[i], suits[k]
);
}
}
}
public static DiceCardPair valueOf(DiceEyes e, CardSuit s) {
return values[e.ordinal()][s.ordinal()];
}
}
你甚至不必直接使用这样的类的实例,例如:
Value v = map.get(DiceCardPair.valueOf(DiceEyes.ONE, CardSuit.SPADES));
if(v == null) {
map.put(DiceCardPair.valueOf(DiceEyes.ONE, CardSuit.SPADES), new Value());
}
答案 4 :(得分:0)
实际问题
如果不引入特定的课程或过多的开销,如何实现这一目标?
已经回答:完全没有。
所以虽然它被明确排除在问题之外,但我想引入一个通用的Pair
类,它可以派上用场,就像这样的案例。
final class Pair<T>
{
public static <T, TT extends T> Pair<T> of(TT t0, TT t1)
{
return new Pair<T>(t0, t1);
}
private final T first;
private final T second;
private Pair(T first, T second)
{
this.first = first;
this.second = second;
}
@Override
public String toString()
{
return "(" + first + "," + second + ")";
}
@Override
public boolean equals(Object object)
{
if(object instanceof Pair)
{
Pair<?> other = (Pair<?>) object;
return
equal(this.first, other.first) &&
equal(this.second, other.second);
}
return false;
}
private static <U> boolean equal(U a, U b)
{
if (a == null)
{
return b == null;
}
return a.equals(b);
}
@Override
public int hashCode()
{
return
((first == null) ? 0 : first.hashCode()) ^
((second == null) ? 0 : second.hashCode());
}
public T getFirst()
{
return first;
}
public T getSecond()
{
return second;
}
}
答案 5 :(得分:0)
使用Pair类作为密钥。你可以在其他地方重复使用它,所以这不是一种矫枉过正。不幸的是,Java没有像其他语言(c ++,c#)那样的内置实现,但是这篇文章中有一个直接的实现: