Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/403.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用密钥复合密钥进行高效HashMap检索(从2个枚举生成)_Java_Performance_Hashmap_Lookup_Composite Key - Fatal编程技术网

Java 使用密钥复合密钥进行高效HashMap检索(从2个枚举生成)

Java 使用密钥复合密钥进行高效HashMap检索(从2个枚举生成),java,performance,hashmap,lookup,composite-key,Java,Performance,Hashmap,Lookup,Composite Key,我有一个2个枚举值,表示到对象的映射,我(当前)正在用一个HashMap建模,其中2个枚举值用作键,对象就是值 这是低效的,因为我正在为Enum1.values()x Enum2.values()的每个组合创建一个新的CompositeKey(Enum1 Enum1,Enum2 Enum2) 我想跳过新的CompositeKey()问题 我目前想到的解决方案是从2个枚举中计算数字表示,类似于int numericKey=enum1.ordinal()*0xFFFF+enum2.ordinal()

我有一个2个枚举值,表示到对象的映射,我(当前)正在用一个HashMap建模,其中2个枚举值用作键,对象就是值

这是低效的,因为我正在为Enum1.values()x Enum2.values()的每个组合创建一个新的CompositeKey(Enum1 Enum1,Enum2 Enum2)

我想跳过新的CompositeKey()问题

我目前想到的解决方案是从2个枚举中计算数字表示,类似于
int numericKey=enum1.ordinal()*0xFFFF+enum2.ordinal()但当我执行
map.get(numricKey)
时,我仍然会自动装箱到整数-因此创建新实例

一个完美的解决方案(IMO)是映射实现(不必是通用的…),但我认为java不存在这样的解决方案

另一个选项可能是
mapping=newobject[Enum1.values().length][Enum2.values().length]
,然后我将使用
Object=mapping[Enum1.ordinal()][Enum2.ordinal()]
进行查找,但这似乎有点“C'ish”

无论如何,运行时性能是这里最重要的方面

欢迎评论

谢谢,
Maxim.

我认为从维护的角度来看,
CompositeKey
方法是最好的,因为它允许您在不改变的情况下使用现有集合。创建这个对象的实例的开销并没有那么高——大多数实例都是短期的,永远不会离开Eden空间(分配和收集都很快)

您还可以实现
Map
,实现类型为
EnumMap
。但是,您必须预先填充“外部”映射,或者编写代码惰性地填充它

但是,如果运行时性能是您的首要任务,那么您使用数组的想法是最好的。但是,我不会将其实现为二维数组。相反,请使用具有以下内容的一维数组和索引:

int index = enum1.ordinal() * _enum1Size + enum2.ordinal;

将其包装到类中,就完成了。

使用
枚举的序数是一个非常糟糕的主意,因为序数是枚举的内部表示,不应在外部代码中使用。关于序数有这样的说法:

大多数程序员不会使用这种方法。它设计用于复杂的基于枚举的数据结构,如EnumSet和EnumMap

我建议使用
EnumMap
,它是专门为您的目的而设计的。创建一个
EnumMap
,并使用枚举的值填充它:

for (Enum1 e1: Enum1.values()) {
    map.put(e1, new EnumMap<Enum2,V>());
    for (Enum2 e2 : Enum2.values()) {
        map.get(e1).put(e2, getSomeValue(e1, e2));
    }
}
for(Enum1 e1:Enum1.values()){
put(e1,new EnumMap());
对于(Enum2 e2:Enum2.values()){
get(e1)、put(e2,getSomeValue(e1,e2));
}
}

我不同意萨卡尔关于使用Enum.ordinal()方法的观点。它是有效和有效的使用。当然,这完全取决于使用范围。如果您可以在代码中应用EnumMap或EnumSet,那么您设计的任何解决方案都将具有相同的运行时范围。您应该避免在当前运行的VM之外使用ordinal()方法,例如序列化、外部化或持久值

最快的解决方案仍然是阵列中计算的偏移量,但可能会占用大量内存。但是,如果使用很少组合的大型枚举(这确实会让人怀疑枚举的正确使用),从而生成一个大型但稀疏的数组,那么原始的“复合”键就是非常接近的第二个

最后,纯粹为了性能,如果您正在形成示例中给出的复合键,
int numericKey=enum1.ordinal()*0xFFFF+enum2.ordinal()

使用更快的移位运算符和或运算符,而不是乘法和加法运算符:
int numericKey=(enum1.ordinal()我有一个名为labels的HashMap,其中一个键的类型为key,两个enum为成员。我注意到使用相同值创建的两个键实例具有不同的哈希代码。重写hashCode()我可以得到相应的值,否则我总是得到null

代码如下:

public static String getMeasureLabel(MeasureSerie measure, TwampMeasure traffic) {
        Key key = new Key(traffic, measure);
        //Key key1 = new Key(traffic, measure); //key.hashCode() != key1.hashCode()
        return labels.get(key); // always returned null
    }
这是钥匙

public static class Key {

        public TwampMeasure trafficType;
        public MeasureSerie measureType;


        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result
                    + ((measureType == null) ? 0 : measureType.getValue().hashCode());
            result = prime * result
                    + ((trafficType == null) ? 0 : trafficType.getValue().hashCode());
            return result;
        }

    }

这是一个我根本没有想到的好主意。谢谢。我将创建
new EnumMap()
作为一个变量,以避免
map。get(e1)
反复获得与外部循环中
new
相同的值。ordinal()如果您不尝试将它存储在JVM之外,并且希望它在您的下一个软件版本中保证可读,那么使用enum是绝对合适的正是为了这种用途——实现性能查找和结构。-1为错误答案。仅供参考,但您更快的移位运算符在1亿次迭代中的速度提高不到1%。此外,我认为您的意思是|(或)而不是&(和)。尊敬的。谢谢你的AND-OR更正,我会更新回复。我实际上得到略高于1%;)我接受。我不接受。这些代码所做的一切都与它很少提供的优化承诺混淆了。我们需要抛弃我们的C编码风格。从长远来看,从整体SE语句来看,编写干净的代码将更加高效。:-)我的前提是纯粹的性能,但既然你想考虑这是一个干净代码的问题,那么它只支持我的情况。这种方法几乎不应该被认为是模糊的,因为它实际上是更干净和自我记录的。如果要对整型字段进行位合并以形成复合值,则使用按位运算符执行此操作。不要用扳手敲打钉子;你可以这么做,但这会让你看起来很傻。仅供参考,但C编码风格与逐位运算符关系不大。恭敬地说,位碰撞起源于汇编语言,然后是C语言,而与语言无关。我以为你不是一个.asm黑客被更多的人理解(#