0

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.

4

4 に答える 4

5

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));
    }
}
于 2010-09-16T20:12:48.893 に答える
1

CompositeKey既存のコレクションを変更せずに使用できるため、メンテナンスの観点からはこのアプローチが最適だと思います。このオブジェクトのインスタンスを作成するオーバーヘッドは実際にはそれほど高くありません。ほとんどのインスタンスは短命であり、Edenスペース(割り当てと収集の両方が非常に迅速に行われます)から出ることはありません。

実装タイプとして、Map<Enum1,Map<Enum2,?>>を実装することもできます。EnumMapただし、「外部」マップを事前に入力するか、コードを記述して遅延して入力する必要があります。

ただし、実行時のパフォーマンスが最優先事項である場合は、アレイを使用するというアイデアが最適です。ただし、2次元配列としては実装しません。代わりに、次の1次元配列とインデックスを使用してください。

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

クラスでそれを包み、あなたは完了です。

于 2010-09-16T20:03:48.457 に答える
1

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();

于 2011-09-01T17:18:49.940 に答える
0

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

    }
于 2013-12-04T10:35:59.367 に答える