0

我在另一个 TreeMap 中使用 TreeMap 作为“键”

IE

TreeMap<TreeMap<String, String>, Object>

在我的代码中,“对象”是一个个人构造,但对于这个例子,我使用了一个字符串。

我创建了一对 TreeMap 来测试TreeMap.CompareTo()TreeMap.HashCode()方法。这从以下开始......

public class TreeMapTest 

public void testTreeMap()
{
TreeMap<String, String> first = new TreeMap<String, String>();
TreeMap<String, String> second = new TreeMap<String, String>();

first.put("one", "une");
first.put("two", "deux");
first.put("three", "trois");
second.put("une", "one");
second.put("deux", "two");
second.put("trois", "three");

TreeMap<TreeMap<String, String>, String> english = new TreeMap<TreeMap<String, String>, String>();
TreeMap<TreeMap<String, String>, String> french = new TreeMap<TreeMap<String, String>, String>();

english.put(first, "english");
french.put(second, "french");

从这里我现在调用英语项目以查看它是否包含密钥

if (english.containsKey(second))
{
System.out.println("english contains the key");
//throws error of ClassCastException: Java.util.TreeMap cannot be cast to
//Java.Lang.Comparable, reading the docs suggests this is the feature if the key is
//not of a supported type.
//this error does not occur if I use a HashMap structure for all maps, why is
//this key type supported for one map structure but not another?
}

但是我应该注意到 HashMap 和 TreeMap 都指向 AbstractMap 父级中的相同 HashCode() 方法。

我的第一个想法是将我的 TreeMap 转换为 HashMap,但这似乎有点笨拙!所以我决定将 hashCode() 方法应用于 2 个树图对象。

int hc1 = first.hashCode();
int hc2 = second.hashCode();



if(hc1 == hc2)
{
systom.out.printline("values are equal " + hc1 + " " + hc2);
}

打印以下内容

values are equal 3877431 & 3877431

对我来说,哈希码应该不同,因为键值不同,我找不到关于 HashMap 和 TreeMap 之间的 hashCode() 方法的实现差异的详细信息。

请不要以下。仅将 Keys 更改为 HashMap 不会停止 ClassCastException 错误。将所有映射更改为 HashMap 即可。所以 TreeMap 中的 containsKey() 方法有些东西不能正常工作,或者我误解了 - 谁能解释一下?

我得到第一个和第二个映射对象的 hashCode 的部分总是产生相同的输出(无论我在这里使用哈希映射还是树映射)但是 if(english.ContainsKey(second)) 不打印任何消息使用了 HashMap,因此 HashMap 实现中的某些内容显然与 compareTo() 方法不同。

我的主要问题是。

在哪里可以找到用于 TreeMap 对象的键类型的详细信息(以防止将来出现“ClassCastException”错误)。

如果我不能使用某种类型的对象作为键,为什么我首先允许将它作为键插入到 TreeMap 中?(当然,如果我可以插入它,我应该能够检查密钥是否存在?)

谁能建议另一个已订购 inster/retrieves 的构造来替换我的 TreeMap 关键对象?

还是我可能发现了奇怪的行为。根据我的理解,我应该能够将 TreeMap 替换为 HashMap,或者我偶然发现了一个边缘场景?

提前感谢您的评论。

大卫。

附言。这个问题在我的代码中不是问题,因为我使用个人实用程序创建依赖于键和值对的哈希(即,我计算键哈希值与值哈希值不同......对不起,如果是一个令人困惑的句子!)我假设 hashCode 方法只是将所有值加在一起,而不考虑项目是键还是值。

pps。我不确定这是否是一个好问题,关于如何改进它的任何指示?

编辑。

从人们的回应中,人们似乎认为我在做一些花哨的语言词典,这对我的例子并不感到意外,对此感到抱歉。我用这个作为例子,因为它很容易进入我的大脑,写得很快,并展示了我的问题。

真正的问题如下。

我正在访问一个遗留的数据库结构,它不能很好地与任何东西交谈(结果集不是正向和反向可读的等)。所以我抓取数据并从中创建对象。最小的对象代表表中的一行(这是在上面的示例中我使用字符串值 'english' 或 'french' 的对象。

我有这些 rowObject 的集合,每一行都有一个明显的键(这是指向相关 rowObject 的 TreeMap)。

我不知道这是否使事情更清楚!

编辑 2。

我觉得我需要进一步详细说明我对原始使用的选择

hashMap<HashMap<String,string>, dataObject>

对于我的数据结构,然后转换为 TreeMap 以获得有序视图。

在编辑 1 中,我说旧版 DB 不能很好地运行(这是我怀疑的 JDBC.ODBC 的问题,我不打算购买 JDBC 来与 DB 通信)。事实上,我在创建 java 'dataObject' 时对数据进行了一些修改。这意味着尽管 DB 可能会以升序或降序的方式输出结果,但我无法知道它们以什么顺序插入到我的 dataObject 中。使用 likeHashMap 似乎是一个不错的解决方案(请参阅 duffymo 的建议),但我后来需要以有序的方式提取数据,而不仅仅是连续提取数据(LinkedHashMap 仅保留插入顺序),而且我不倾向于对所有内容进行排序和当我需要在另外两个项目之间插入一个新项目时制作副本,TreMap 会为我做这件事...... 但是如果我为键创建一个特定对象,它将只包含一个 TreeMap 作为成员,显然我需要提供一个 compareTo 和 hashCode 方法。那么为什么不只是扩展 TreeMap (尽管 Duffymo 有一点关于抛出该解决方案)!

4

3 回答 3

6

不是一个好主意。映射键必须是不可变的才能正常工作,而您的则不是。

你真正想做什么?当我看到人们用数据结构做这样的事情时,我觉得他们真的需要一个对象,但却忘记了 Java 是一种面向对象的语言。

看起来你想要一个粗略的字典来翻译语言。我会创建一个LanguageLookup嵌入这些地图的类,并提供一些方法让用户更容易与之交互。更好的抽象和封装,更多的信息隐藏。这些应该是您的设计目标。考虑如何添加除英语和法语之外的其他语言,以便您可以在其他上下文中使用它。

public class LanguageLookup { 
    private Map<String, String> dictionary;

    public LanguageLookup(Map<String, String> words) {
        this.dictionary = ((words == null) ? new HashMap<String, String>() : new HashMap<String, String>(words));
    }

    public String lookup(String from) {
        return this.dictionary.get(from);
    }

    public boolean hasWord(String word) { 
        return this.dictionary.containsKey(word);
    }
}

在您的情况下,您似乎想将一个英语单词翻译成法语,然后查看法语词典是否包含该单词:

Map<String, String> englishToFrenchWords = new HashMap<String, String>();
englishToFrenchWords.put("one", "une");
Map<String, String> frenchToEnglishWords = new HashMap<String, String>();
frenchToEnglishWords.put("une", "one");
LanguageLookup englishToFrench = new LanguageLookup(englishToFrenchWords);
LanguageLookup frenchToEnglish = new LanguageLookup(frenchToEnglishWords);
String french = englishToFrench.lookup("one");
boolean hasUne = frenchToEnglish.hasWord(french);
于 2012-07-10T11:37:40.150 回答
2

您的 TreeMap 不是 Comparable,因此您无法将其添加到 SortedMap 并且它不是不可变的,因此您无法将其添加到 HashMap。您可以使用 IdentityMap 但怀疑 EnumMap 是更好的选择。

enum Language { ENGLISH, FRENCH }

Map<Language, Map<Language, Map<String, String>>> dictionaries = 
                                                    new EnumMap<>(Language.class);

Map<Language, Map<String, String>> fromEnglishMap = new EnumMap<>(Language.class);
dictionaries.put(Language.ENGLISH, fromEnglishMap);
fromEnglishMap.put(Language.FRENCH, first);

Map<Language, Map<String, String>> fromFrenchMap = new EnumMap<>(Language.class);
dictionaries.put(Language.FRENCH, fromFrenchMap);
fromEnglishMap.put(Language.ENGLISH, second);

Map<String, String> fromEnglishToFrench= dictionaries.get(Language.ENGLISH)
                                             .get(Language.FRENCH);
于 2012-07-10T11:57:18.940 回答
0

对于 Hashmap 有效而 Treemap 无效的问题:

Treemap 是“排序映射”,这意味着条目是根据键排序的。这意味着通过实现Comparable接口,密钥必须是可比较的。地图通常不会实现这一点,我强烈建议您不要创建自定义类型来添加此功能。正如 duffymo 所提到的,使用地图作为键是一个主意。

于 2012-07-10T11:46:07.307 回答