Java 适当地支持不区分大小写的映射

Java 适当地支持不区分大小写的映射,java,Java,我想实现一个不区分大小写的哈希映射。这个问题本身并不是新问题,但我想添加额外的功能,不知道应该采取什么总体方向。我希望客户能够这样做: boolean preserve_case = true; Map<String, MyClass> maplet = new CaseInsensitiveHashMap<MyClass>(preserve_case); // If the client enters true at construction, then the put

我想实现一个不区分大小写的哈希映射。这个问题本身并不是新问题,但我想添加额外的功能,不知道应该采取什么总体方向。我希望客户能够这样做:

boolean preserve_case = true;
Map<String, MyClass> maplet = new CaseInsensitiveHashMap<MyClass>(preserve_case); // If the client enters true at construction, then the put, get, and remove methods should still be case insensitive, but the entry and key sets should preserve the case that the client used when calling put.

maplet.put("FoO", my_class);

MyClass bar = maplet.get("foo"); // Should return a reference to my_class

Set<Entry<String, MyClass>> case_sensitive_set = maplet.entrySet(); // Since the client input true to preserve order, this entry set should be ["FoO"=my_class.toString()]
boolean preserve\u case=true;
Map maplet=new CaseInsensitiveHashMap(保留大小写);//如果客户端在构造时输入true,那么put、get和remove方法仍应不区分大小写,但条目和键集应保留客户端在调用put时使用的大小写。
maplet.put(“FoO”,我的班级);
MyClass bar=maplet.get(“foo”);//应该返回对my_类的引用
Set区分大小写\u Set=maplet.entrySet();//由于客户端输入true以保留顺序,因此该条目集应为[“FoO”=my_class.toString()
我能很好地处理大部分事情;我只需在后端保留一个
HashMap
。当客户机放入任何内容时,我会在将键添加到映射之前将其大写

我只是很难编写
keySet()
entrySet()方法。我希望返回的条目集和键集由映射支持,这是Java映射的标准

但是,我能想到的处理这个问题的唯一方法是创建第二个备份数据结构,类似于
保留的\u case\u映射
,它包含
input.toUpperCase()
=>作为键值对的输入。当客户端调用
entrySet()
(或
keySet()
)时,我可以通过循环
保留的大小写映射来构造返回的条目集。这里的问题是,如果我更改了
HashMap
,返回的条目集将不会被修改,除非我误解了什么


让我知道这是否有意义,或者我是否在纠结一个简单的情况。

不只是存储值,而是存储输入键(仍然有用户提供的大小写)和值的KeyValuePair,并使用大写键作为键


提取值时,请在返回之前从KeyValuePair中提取该值。您还可以通过对大写键执行相同的查找,然后取出原始键来检索原始键的大小写。

一种方法是从
HashMap
继承,然后在对键调用
toUpperCase()
后调用基类中的方法

但是,只重写
MyClass
中的
hashCode()
方法(或您将使用的任何类),然后使用标准的
HashMap
类,而不实现您自己的类,要容易得多。例如:

public int hashCode() {
    return this.toString().toUperCase().hashCode();
}

如果你把它放在一个基类中,然后让你的类继承它,那么你就有了一个非常简单的解决方案。

为了简化地图的包装,你可以使用Google Guava库中的,但这是可选的

在从备份映射放置/获取某些内容之前,请将字符串键包装到一个覆盖hashCode()/equals()的类中,并将包装器用作映射中的键。比如:

class KeyWrapper { 
    int hashCode() { 
        return actualStringKey.toUpperCase().hashCode()
    }
    boolean equals(Object o) {...} // compare case-insensitive
}

如果覆盖keySet(),则可以创建一个新的集,并用actualStringKeys填充它。

我为已编写的映射缓存做了以下工作。此处更改了命名以反映您的案例不敏感,而不是我的缓存,但概念是相同的

这将为您提供一个
UncasedMap
类,该类可以重新用于保存任何内容,但仅限于使用字符串作为键

// Map that uses case insensitive strings as keys to any kind of element
public class UncasedMap<V> extends HashMap<String, V>
{
    private static final long serialVersionUID = 1L;
    private Map<String, MappedObject> backingMap = new HashMap<String, MappedObject>();

    @Override
    public V put(String key, V value)
    {
        MappedObject currentVal = backingMap.get( key.toUpperCase() );
        backingMap.put( key.toUpperCase(), new MappedObject(key.toUpperCase(), value) );
        return currentVal.getElement();

    }
    @Override
    public V get(Object key)
    {
        return backingMap.get(key.toString().toUpperCase()).getElement();
    }

    // a private inner class of my case insensitive map, to wrap the callers element
    private class MappedObject
    {
        private String key;
        private String orig_key;
        private V mapElement;

        public MappedObject(String keyx, V elem)
        {
            this.orig_key = keyx;
            this.key = keyx.toUpperCase();
            this.mapElement = elem;
        }

        public V getElement()
        {
            return this.mapElement;
        }
    }
}
//使用不区分大小写的字符串作为任何类型元素的键的映射
公共类UncasedMap扩展了HashMap
{
私有静态最终长serialVersionUID=1L;
私有映射backingMap=newhashmap();
@凌驾
公共V put(字符串键,V值)
{
MappedObject currentVal=backingMap.get(key.toUpperCase());
backingMap.put(key.toUpperCase(),新MappedObject(key.toUpperCase(),value));
返回currentVal.getElement();
}
@凌驾
public V get(对象键)
{
return backingMap.get(key.toString().toUpperCase()).getElement();
}
//我的不区分大小写映射的私有内部类,用于包装callers元素
私有类MappedObject
{
私钥;
私有字符串源密钥;
私有V映射元素;
公共MappedObject(字符串keyx,V elem)
{
this.orig_key=keyx;
this.key=keyx.toUpperCase();
this.mapement=elem;
}
public V getElement()
{
返回此.mapement;
}
}
}

实现
keySet()
emptySet()
将更加复杂,但实现过程相同。

您可以使用带有不区分大小写比较器的树映射。树映射将使用比较器以不区分大小写的方式比较键:

    Map<String, Integer> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

    map.put("Foo", 1);
    map.put("fOo", 2);

    System.out.println(map.get("foo")); // prints 2
    System.out.println(map.keySet()); // prints [Foo]
Map Map=newtreemap(String.CASE不区分大小写顺序);
地图.put(Foo,1);;
地图.put(fOo,2);;
System.out.println(map.get(“foo”);//印刷品2
System.out.println(map.keySet());//印刷品[Foo]

您可以使用Apache Commons集合中的CaseInsensitiveMap


这是一个好主意,尽管我希望允许客户端输入任意类。但是,如果我创建了一个短的包含类,我是否可以重写put方法以进行类似的处理,并使用类似key.toUpperCase().hashCode()的哈希代码存储密钥?更明确地说,我是否可以重写备份数据结构上的哈希查找,以便发送传入的密钥toUpperCase()?但是由于客户端(使用不区分大小写映射的程序员)可以为
MyClass
提供任何类,您必须要求它们重写hashCode和equals。重写MyClass中的hashCode不会有任何帮助,因为String是映射的键。String是最后一个类。这看起来肯定是有益的,但需要导入一个大的libr