简体   繁体   English

Java - 将不同类型的2个键映射到相同的值(任何类型)。 我该怎么办呢?

[英]Java - map 2 keys of different types to the same value (any type). How can I go about it?

OK here's the scenario. 好的,这是场景。 I have static pairs of keys that always go together . 我有一对总是在一起的 静态键。 One key is the index (aka int ) and one key is the description (aka string or enum ). 一个键是索引(又名int ),一个键是描述(又名stringenum )。 I know all these keys in advance so the only thing that's really changing are the values, new keys are never added. 事先知道所有这些键所以唯一真正改变的是值,永远不会添加新键。

The values however can be any type: string, int, long. 但是,值可以是任何类型:string,int,long。 Some values are not singular and are made of more than one value. 有些值不是单数,而是由多个值组成。 I do know however, in advance, to which type of value each key pair will point. 但是,我事先知道每个密钥对将指向哪种类型的值。

The values will most likely always be set using the index. 很可能始终使用索引设置值。 However, I want to be able to access the values quickly (no loops please, and hopefully no casting either) by either the index(int) or the description (string/enum). 不过,我希望能够迅速访问值(无环路请,并希望没有铸造要么)由一指数(INT)或描述(串/ ENUM)。 Also, when accessing the values via the index I should also have access to the description 此外,当通过索引访问值时,我也应该有权访问描述

This might make things clearer: 这可能会使事情变得更清楚:

1/Name ----> "danny"        //1 and Name are known in advance and always go together. also, they always point to a string
2/Age  ----> 24             //2 and Age are known in advance and always go together. also, they always point to an int
3/Time ----> 352343463463L  //3 and Time are known in advance and always go together. also, they always point to a long
4/Occupation    ---> [description] "magician"
                ---> [type] "entertainer"
                ---> [years] 3              //4 and Status are known in advance and always go together. also they will always point to 2 strings and and an int (or an object contraining 2 strings and an int...)

Functionality needed: 所需功能:

set(1, "Jasmine");
get(1);             //returns "Jasmine"
get(Name);          //return "Jasmine"  (name can be either string or enum I suppose)
getDescription(1);  // returns Name (again, name could be either string or enum). this function could possibly be merged with get(1) to have it return both description and value in the first place.

set(2, 32);
get(2);             //returns 32
get(Age);            //returns 32

create 创造

class Entry {
  int index;
  String description;
  Object value;
}

declare 2 HashMaps: 声明2个HashMaps:

HashMap<Integer, Entry> idxValue=new HashMap<Integer, Entry>();
HashMap<String, Entry> descrValue=new HashMap<String, Entry>();

define methods to store and retrieve entries and values which work with both tables. 定义存储和检索与两个表一起使用的条目和值的方法。

You can use my TypedMap for this. 你可以使用我的TypedMap In a nutshell, it gives you a type-safe map which can store any kind of object as value under a key: 简而言之,它为您提供了一个类型安全的映射,可以将任何类型的对象存储为键下的值:

TypedMap map = new TypedMap();

String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 ); // Look Ma, no cast!
assertEquals( expected, value );

List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 ); // Even with generics
assertEquals( list, valueList );

To give you fast access to the typed keys, I suggest to use an enum: 为了让您快速访问键入的键,我建议使用枚举:

enum Key {
    Name(1) { @Override public TypedMapKey<String> getKey() { return NAME_KEY; },
    ...;

    private static Key[] byIndex = new Key[MAX_INDEX+1];
    static {
        for( Key key : values() ) { byIndex[key.index] = key; }
    }

    public static byIndex(int index) {
        return byIndex[index]; // I suggest non-null checks here if you have gaps
    }

    private Key(int index) {
        this.index = index;
    }

    public TypedMapKey<?> getKey() { throw new UnsupportedOperationException( "Please override"; ) }
}

UPDATED Here's my definitive answer: 更新这是我的最终答案:

You have a static relationship between an index, a description and the type of value that is associated with it. 索引,描述和与其关联的值的类型之间存在静态关系。 So let's capture this in a class: 所以让我们在课堂上捕捉这个:

public class Key<VALUETYPE> {
    private final Integer          index;
    private final String           description;
    private final Class<VALUETYPE> valueType;

    public Key(final Integer index, final String description, final Class<VALUETYPE> valueType) {
        super();
        this.index = index;
        this.description = description;
        this.valueType = valueType;
    }

    public Integer getIndex() {
        return index;
    }

    public String getDescription() {
        return description;
    }

    public Class<VALUETYPE> getValueType() {
        return valueType;
    }

    @Override
    public int hashCode() {
        return index.hashCode();
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) { return true; }
        if (obj == null) { return false; }
        if (getClass() != obj.getClass()) { return false; }
        Key<?> other = (Key<?>) obj;
        return index.equals(other.index);
    }
}

I've assumed the indexes to be unique identifiers of the key, so have based hashCode and equals on it. 我假设索引是密钥的唯一标识符,因此基于hashCode并且equals它。

Now you write an accessor class that covers your use cases: 现在,您编写了一个涵盖您的用例的访问者类:

public class MapAccessor {
    private final Map<Integer, Key<?>> keyMap;
    private final Map<Key<?>, Object>  valueMap;

    public MapAccessor(final Map<Integer, Key<?>> keysByIndex, final Map<Key<?>, Object> valueMap) {
        this.keyMap = keysByIndex;
        this.valueMap = valueMap;
    }

    public void put(final Integer index, final Object value) {
        Key<?> key = keyMap.get(index);
        if (key.getValueType().isInstance(value) || value == null) {
            valueMap.put(key, value);
        }
        else {
            throw new IllegalArgumentException("Wrong type of value for index " + index + ", expected: " + key.getValueType()
                    + ", actual: " + value.getClass());
        }
    }

    public <VALUETYPE> VALUETYPE get(final Key<VALUETYPE> key) {
        return key.getValueType().cast(valueMap.get(key));
    }

    public Object get(final Integer index) {
        Key<?> key = getKey(index);
        return key == null ? null : get(key);
    }

    public Key<?> getKey(final Integer index) {
        return keyMap.get(index);
    }

    public String getDescription(final Integer index) {
        Key<?> key = getKey(index);
        return key == null ? null : key.getDescription();
    }
}

Alternatively, you could put this in a subclass of HashMap<Key<?>, Object> instead of delegating to it. 或者,您可以将它放在HashMap<Key<?>, Object>的子类中HashMap<Key<?>, Object>而不是委托给它。

Let's demonstrate how to use the above. 让我们演示如何使用上面的内容。 Note that a cast is unavoidable for lookup by index. 请注意,通过索引进行查找是不可避免的。

public class ExampleUsage {
    private static final Key<String>   NAME  = new Key<>(1, "Name", String.class);
    private static final Key<Integer>  AGE   = new Key<>(2, "Age", Integer.class);

    private static final Map<Integer, Key<?>> keysByIndex = buildKeysByIndex(NAME, AGE);

    public static void main(final String... args) {
        Map<Key<?>, Object> valueMap = new HashMap<>();

        MapAccessor accessor = new MapAccessor(keysByIndex, valueMap);

        accessor.put(1, "Jasmine");
        String nameByIndex = (String) accessor.get(1); // returns "Jasmine", cast can't be avoided
        String nameByKey = accessor.get(NAME); // returns "Jasmine", no cast necessary
        Key<?> nameKeyByIndex = accessor.getKey(1); // returns NAME
        String nameDescriptionByIndex = accessor.getDescription(1); // returns "Name"

        accessor.put(2, 32);
        Integer ageByIndex = (Integer) accessor.get(2); // returns 32, cast can't be avoided
        Integer ageByKey = accessor.get(AGE); // returns 32, no cast necessary
        Key<?> ageKeyByIndex = accessor.getKey(2); // returns AGE
        String ageDescriptionByIndex = accessor.getDescription(2); // returns "Age"
    }

    private static Map<Integer, Key<?>> buildKeysByIndex(final Key<?>... keys) {
        Map<Integer, Key<?>> keyMap = new HashMap<Integer, Key<?>>();
        for (Key<?> key : keys) {
            keyMap.put(key.getIndex(), key);
        }
        return Collections.unmodifiableMap(keyMap);
    }
}

Create a simple class to hold the data and make things like Name , Age , etc. bean-like properties with a backing field: 创建一个简单的类来保存数据,并使用支持字段创建NameAge等类似bean的属性:

private int age; 

public getAge() { return age; } 

public setAge(int value) { age = value; }

Make a custom annotation called @FieldInfo which contains a field ordinal and a field description: 创建一个名为@FieldInfo的自定义注释,其中包含字段序号和字段描述:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)    
public @interface FieldInfo {
    int ordinal();
    String description();
}

Decorate the data class with your annotation: 使用注释装饰数据类:

@FieldInfo(ord=1, description="Age")
private int age; 

public getAge() { return age; } 

public setAge(int value) { age = value; }

Make a base class for your data class which contains universal get() and set() methods which can get/set values using the field ordinal and a getDescription() method which returns the field description given the ordinal. 为您的数据类创建一个基类,该类包含通用的get()set()方法,这些方法可以使用字段序号获取/设置值,getDescription()方法返回给定序号的字段描述。 Use this.getClass().getDeclaredFields() to get Fields[] using reflection, and then use Field.getAnnotation(FieldInfo.class) to get the annotation for each field. 使用this.getClass().getDeclaredFields()使用反射获取Fields[] ,然后使用Field.getAnnotation(FieldInfo.class)获取每个字段的注释。

Since data class properties don't change at runtime you can build two static lookups <int, Field> and <int, String> for each data class type in a static constructor to speed things up if you are frequently using ordinals to access fields. 由于数据类属性在运行时不会更改,因此您可以为静态构造函数中的每个数据类类型构建两个静态查找<int, Field><int, String>以便在经常使用序数访问字段时加快速度。 Using this approach you can further describe fields by extending the annotation and your data class is still a simple class with traditional getters/setters. 使用此方法,您可以通过扩展注释来进一步描述字段,并且您的数据类仍然是使用传统getter / setter的简单类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM