【问题标题】:Java TreeMap (comparator) and get method ignoring the comparatorJava TreeMap(比较器)和忽略比较器的get方法
【发布时间】:2010-12-19 10:54:05
【问题描述】:
public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    };

private Map< String, Animal > _animals = new TreeMap< String, Animal >(ID_IGN_CASE_COMP);

我的问题是,如何使用方法 get(id) 忽略给定的比较器。我希望地图按不区分大小写进行排序,但是当我通过给定键获取值时,我希望它区分大小写。

【问题讨论】:

  • 这应该如何工作? TreeMap 需要表示两种不同的顺序。它不能那样做。
  • 简短评论:您不必创建此 Comparator 它已存在于 java.lang.String 类中:String.CASE_INSENSITIVE_ORDER
  • 我没有看到问题。它将通过字符串键大小写敏感地获取值。
  • 难道不能通过覆盖get方法来实现吗?

标签: java comparator treemap


【解决方案1】:

我认为答案很简单。实现您自己的比较器,该比较器执行不区分大小写的排序,但 NOT 是否为“A”和“a”返回 0... 也对它们进行排序。

问题是您的比较器在 compare("A", "a") 情况下返回 0,这意味着就地图而言,它是相同的键。

使用比较器,例如:

public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

    public int compare(String s1, String s2) {
        int result = s1.compareToIgnoreCase(s2);
        if( result == 0 )
            result = s1.compareTo(s2);
        return result;
    }
};

那么不管大小写,所有的键都会进入,“a”和“A”仍然会被排序在一起。

换句话说,get("a") 会给你一个与 get("A") 不同的值...并且它们都会出现在 keySet() 迭代器中。它们将被排序在一起。

【讨论】:

  • 这听起来不错,谢谢。我将对其进行测试,因为我意识到我在使用 put 方法时也需要考虑到这一点。因为我也需要区分“A”和“a”。我会测试它并告诉你。
  • 非常感谢,它解决了我的两个问题。这正是我想要的答案。再次感谢:)
  • 很高兴它成功了。有时简单的答案是正确的。 :)
  • 使用谷歌收藏:静态最终比较器 ID_IGN_CASE_COMP = Ordering.from(String.CASE_INSENSITIVE_ORDER).compound(Ordering.natural());
【解决方案2】:

在 TreeMap 中,添加两个键 a 和 b(按此顺序)以便 compare(a, b) 返回 0 将导致最新添加的条目 (b) 将覆盖第一个条目 (a)。

在您的情况下,这意味着不区分大小写的 get(id) 永远不会有任何用途。

引用http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html

请注意,如果该有序映射要正确实现 Map 接口,则有序映射维护的顺序(无论是否提供显式比较器)必须与 equals 一致。 (参见 Comparable 或 Comparator 以获得与 equals 一致的精确定义。)这是因为 Map 接口是根据 equals 操作定义的,但是 map 使用它的 compareTo(或 compare)方法执行所有键比较,所以两个键从排序图的角度来看,这种方法认为相等的元素是相等的。已排序映射的行为是明确定义的,即使它的排序与 equals 不一致;它只是不遵守 Map 接口的一般约定。

这可能不是你想要的。

如果映射比较小,并且您不需要多次获取已排序的条目,则解决方案是使用 HashMap(或未显式设置比较器的 TreeMap),并在以下情况下不区分大小写地对条目进行排序您需要订购它们。

【讨论】:

    【解决方案3】:

    为此,您必须使用两个单独的 TreeMap,它们的内容相同,但比较器不同。

    【讨论】:

    • 这只是占用太多空间...我宁愿获得一组密钥并比较它们,但正在寻找一种最简单的方法
    【解决方案4】:

    也许它会完成这项工作:

        new Comparator<String>(){
        public int compare(String s1, String s2)
        {
            String s1n = s1.toLowerCase();
            String s2n = s2.toLowerCase();
    
            if(s1n.equals(s2n))
            {
                return s1.compareTo(s2);
            }
            return s1n.compareTo(s2n);
        }
    };
                                                        }
    

    【讨论】:

    • +1 你必须为每个字符而不是整个字符串做小写。但是方向是对的。它的行为与不区分大小写的比较器不同(对于不区分大小写的比较器,键“a”、“A”相等)。
    【解决方案5】:

    您需要一个multimap:这个multimap 的每个条目都保留不区分大小写 键和另一个以原始键作为值的映射。

    有许多可免费使用的多映射实现,例如Common CollectionsGoogle Collections

    【讨论】:

      【解决方案6】:

      除了所有其他答案并同意之外,不可能拥有具有不同比较器的单个 TreeMap 结构:

      根据您的问题,我了解到您有两个要求:数据模型应区分大小写(您在使用 get() 时需要区分大小写的值),演示者应不区分大小写(您需要区分大小写的排序,演示只是一个假设)。

      假设,我们用映射 (aa,obj1), (aA,obj2), (Aa,obj3), (AA,obj4) 填充 Map。迭代器将按顺序提供值:(obj4, obj3, obj2, obj1)(*)。现在,如果地图的排序不区分大小写,您期望哪个顺序?所有四个键都是相等的,并且顺序未定义。或者您是否正在寻找一种解决方案来解决键“AA”的集合 {obj1, obj2, obj3, obj4}?但这是一种不同的方法。

      SO 鼓励社区诚实:因此我在这一点上的建议是再次查看您的要求 :)

      (*) 未测试,假设 'A'

      【讨论】:

        【解决方案7】:

        在循环中使用floorEntry 然后higherEntry 以不区分大小写的方式查找条目;当您找到完全匹配的键时停止。

        【讨论】:

        • 这到底是如何工作的?我得到了 floorEntrry 将返回搜索到的键的部分,但是,如果搜索到的键是“aa1”,它可以找到“Aa1”或“AA1”,更高的条目给我另一个
        • 你是对的 - 它不起作用,因为你需要地图使用区分大小写的比较器(不是你想要的)并且从 key.toLowerCase(Locale) 开始不会可靠的不同语言。接受的答案是得到你想要的东西的方式。
        猜你喜欢
        • 1970-01-01
        • 2018-06-30
        • 1970-01-01
        • 2013-12-29
        • 2013-12-08
        • 2022-01-19
        • 2018-07-05
        • 2015-03-12
        • 2021-11-18
        相关资源
        最近更新 更多