使用键复合键进行高效的 HashMap 检索(由 2 个枚举构建)

发布于 2024-09-19 17:37:57 字数 696 浏览 2 评论 0 原文

我有 2 个枚举值,表示到对象的映射,我(当前)使用 HashMap 对其进行建模,其中 2 个枚举值用作键,对象是值。

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

我想跳过新的 CompositeKey() 问题。

我目前想到的解决方案是计算 2 个枚举的数字表示,例如 int numericKey = enum1.ordinal() * 0xFFFF + enum2.ordinal(); ,但是当我' ll do map.get(numricKey) 我仍然会自动装箱为 Integer - 因此创建新实例。

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

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

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

欢迎评论。

谢谢, 格言。

I have a 2 enum values representing a mapping to object which I'm (currently) modeling with a HashMap with the 2 enums values are used as key and the object is the value.

This is inefficient because what I'm doing is creating a new CompositeKey(Enum1 enum1, Enum2 enum2) for each combination of the Enum1.values() x Enum2.values().

I would like to skip the new CompositeKey() issue.

The solution I'm currently think of is Calculation a number representations of from the 2 enums, something like int numericKey = enum1.ordinal() * 0xFFFF + enum2.ordinal(); but then when I'll do map.get(numricKey) I will still be auto boxing to Integer - hence creation new instances.

A perfect solution (IMO) would be Map implementation (doesn't have to be generic...) but I don't think such existing for java.

Another option is probably mapping = new Object[Enum1.values().length][Enum2.values().length] which I will then do lookup with Object = mapping[enum1.ordinal()][enum2.ordinal()] but that seems a bit too "C'ish".

Anyways, runtime performance is the most important aspect here.

Comments are welcome.

Thanks,
Maxim.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雪花飘飘的天空 2024-09-26 17:37:58

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

大多数程序员不会使用此方法。它设计用于复杂的基于枚举的数据结构,例如 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));
    }
}

Using the ordinal of an enum is a very bad idea because the ordinal is an internal representation of an enum and should never be used in external code. The Enum specification has this to say about ordinal:

Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.

I recommend using an EnumMap which is specifically designed such purposes as the one you have. Create an EnumMap<Enum1,EnumMap<Enum2,V>> and populated it using the values of your enums:

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));
    }
}
睫毛溺水了 2024-09-26 17:37:58

我认为从维护的角度来看,CompositeKey 方法是最好的,因为它允许您不加修改地使用现有集合。创建该对象的实例的开销实际上并没有那么高——大多数实例都是短暂的,并且永远不会离开伊甸园空间(它的分配和收集都非常快)。

您还可以实现 Map>,并使用 EnumMap 作为实现类型。但是,您必须预先填充“外部”映射,或者编写代码来延迟填充它。

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

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

将其包装在一个类中,然后就完成了。

I think the CompositeKey approach is the best from a maintenance perspective, because it lets you use the existing collections unchanged. The overhead of creating instances of this object really isn't that high -- most of the instances will be short-lived and never get out of the Eden space (which is both allocated and collected very quickly).

You could also implement Map<Enum1,Map<Enum2,?>>, with EnumMap as your implementation type. However, you'll have to either pre-populate the "outer" maps, or write code to lazily populate it.

But if runtime performance is your top priority, then your idea of using an array is best. I wouldn't implement it as a two-dimensional array, however. Instead, use a one-dimensional array and index with the following:

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

Wrap it in a class and you're done.

以歌曲疗慰 2024-09-26 17:37:58

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

最快的解决方案仍然是计算数组内的偏移量,但可能会使用潜在的大量内存。但是,如果您有大量的枚举(这确实会让人怀疑 Enum 的正确使用),而组合很少,导致数组很大但稀疏,那么您原来的“复合”键紧随其后。

最后,纯粹为了性能,如果您要形成示例中给出的复合键,int numericKey = enum1.ordinal() * 0xFFFF + enum2.ordinal();
使用更快的移位运算符和 OR 运算符,而不是乘法和加法运算符: int numericKey = (enum1.ordinal() << 16) | enum2.ordinal();

I disagree with Sarkar's point on the use of Enum.ordinal() method. It is effective and valid to use. Of course, this is based entirely on the scope of use. If you can apply the use of EnumMap or EnumSet in your code, then any solution you devise will have the same runtime scope. You should avoid use of the ordinal() method outside the current running VM, e.g. serialization, externalize or a persistent value.

The fastest solution still remains the calculated offset within an array, but could use potentially large amounts of memory. But if you have large enumerations (which would really call into doubt the appropriate use of an Enum) with very few combinations, resulting in a large but sparse array, then your original "compound" key is a very close second.

Finally, purely for performance, if you are forming the compound key given in your example, int numericKey = enum1.ordinal() * 0xFFFF + enum2.ordinal();
use the faster shift operator and OR operator rather than the multiply and add operators: int numericKey = (enum1.ordinal() << 16) | enum2.ordinal();

挽梦忆笙歌 2024-09-26 17:37:58

我有一个名为 labels 的 HashMap,其键类型为 Key,并有两个枚举作为成员。我注意到使用相同值创建的两个 Key 实例具有不同的哈希码。重写 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;
        }

    }

I have a HashMap named labels with a key of type Key with two enum as members. I noticed that two instances of Key created with same values had different hash codes. Overriding hashCode() I am able to get the corrisponding value, otherwise I always got null.

Here is the code:

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
    }

And here is Key

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;
        }

    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文