我不确定在地图中使用动态对象(如集合作为键)的主流观点。
我知道典型的Map实现(例如,HashMap)使用哈希码来决定将条目放入哪个桶,如果该哈希码应该以某种方式改变(可能因为Set的内容应该发生变化,那么通过导致错误地计算存储桶(与最初将Set插入HashMap的方式相比),可能会弄乱HashMap。
但是,如果我确保Set内容根本没有变化,那么这是否可行?即便如此,这种方法通常被认为是容易出错的,因为集合具有固有的易变性(即使采取了预防措施以确保它们不被修改)?
看起来Java允许将函数参数指定为final;这可能是一个可以采取的一个小小的预防措施?
人们甚至在商业/开源实践中做这样的事情吗? (将List,Set,Map等作为键放在地图中?)
我想我应该描述一下我正在努力实现的目标,这样才能使动机变得更加清晰,也许可以提出替代实施方案。
我想要完成的是拥有这样的东西:
class TaggedMap<T, V> {
Map<Set<T>, V> _map;
Map<T, Set<Set<T>>> _keys;
}
...本质上,能够用某些键(T)“标记”某些数据(V)并写入其他辅助功能来访问/修改数据并用它做其他奇特的东西(即返回一个列表)满足一些密钥标准的所有条目)。 _keys的功能是作为一种索引,以便于查找值而无需遍历所有_map的条目。
在我的情况下,我打算专门使用T = String,V = Integer。我与之交谈的人建议用字符串代替Set,即:
class TaggedMap<V> {
Map<String, V> _map;
Map<T, Set<String>> _keys;
}
其中_map中的键的类型为“key1; key2; key3”,其中键由分隔符分隔。但我想知道我是否可以完成一个更通用的版本,而不必在密钥之间强制执行带分隔符的字符串。
我想知道的另一件事是,是否有某种方法可以将其作为Map扩展。我想象的是:
class TaggedMap<Set<T>, V> implements Map<Set<T>, V> {
Map<Set<T>, V> _map;
Map<T, Set<Set<T>>> _keys;
}
然而,我无法将其编译,可能是由于我对泛型的理解较差。以此作为目标,任何人都可以修复上述声明,以便它根据我所描述的精神工作或建议一些轻微的结构修改?特别是,我想知道“实施地图,V&gt;”是否可以声明这样一个复杂的接口实现。
答案 0 :(得分:9)
如果你确定
,那是对的Set
内容未被修改,Set
本身未被修改将它们用作Map
中的键是完全安全的。
很难确保(1)不会意外违反。一种选择可能是专门设计存储在Set
内的类,以便该类的所有实例都是不可变的。这可以防止任何人意外更改其中一个Set
键,因此(1)将无法实现。例如,如果您使用Set<String>
作为密钥,则无需担心由于外部修改而导致String
内的Set
更改。
您可以使用Collections.unmodifiableSet
方法轻松地使(2)成为可能,该方法返回无法修改的Set
的包装视图。这可以对任何Set
进行,这意味着对你的密钥使用这样的东西可能是一个非常好的主意。
希望这有帮助!如果您的用户名意味着我的想法,那么学习每种语言都会好运! : - )
答案 1 :(得分:0)
正如您所提到的,集合可以更改,即使您阻止集合更改(即它包含的元素),元素本身也可能会发生变化。那些因素进入哈希码。
你能用更高层次的术语描述你想要做什么吗?
答案 2 :(得分:0)
@ templatetypedef的答案基本上是正确的。如果集合的状态在密钥期间无法更改,则只能在某些数据结构中安全地使用Set
作为密钥。如果集合的状态发生变化,则会违反数据结构的不变量,对其进行操作会得到错误的结果。
使用Collections.unmodifiableSet
创建的包装器可以提供帮助,但有一个隐藏的问题。如果原始集仍然可以直接访问,则应用程序可以对其进行修改; e.g。
public void addToMap(Set key, Object value);
someMap.put(Collections.unmodifiableSet(key), value);
}
// but ...
Set someKey = ...
addToMap(someKey, "Hi mum");
...
someKey.add("something"); // Ooops ...
为了保证不会发生这种情况,您需要在包装之前对该集进行深层复制。这可能很昂贵。
使用Set
作为关键字的另一个问题是它可能很昂贵。实现键/值映射有两种通用方法;使用hashcode
方法或使用实现排序的compareTo
方法。这两套都很贵。