关于java:不区分大小写的字符串作为HashMap键

Case insensitive string as HashMap key

我想使用不区分大小写的字符串作为HashMap键,原因如下。

  • 在初始化期间,我的程序使用用户定义的String创建HashMap
  • 在处理事件(在我的情况下是网络流量)时,我可能在不同的情况下收到String但我应该能够从HashMap找到而忽略我从流量中收到的情况。

我遵循了这种方法

CaseInsensitiveString.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    public final class CaseInsensitiveString {
            private String s;

            public CaseInsensitiveString(String s) {
                            if (s == null)
                            throw new NullPointerException();
                            this.s = s;
            }

            public boolean equals(Object o) {
                            return o instanceof CaseInsensitiveString &&
                            ((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
            }

            private volatile int hashCode = 0;

            public int hashCode() {
                            if (hashCode == 0)
                            hashCode = s.toUpperCase().hashCode();

                            return hashCode;
            }

            public String toString() {
                            return s;
            }
    }

LookupCode.java

1
    node = nodeMap.get(new CaseInsensitiveString(stringFromEvent.toString()));

因此,我正在为每个事件创建一个CaseInsensitiveString的新对象。 因此,它可能会影响性能。

有没有其他方法可以解决这个问题?


1
2
Map<String, String> nodeMap =
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

这就是你真正需要的一切。


正如GuidoGarcía在回答中所建议的那样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.HashMap;

public class CaseInsensitiveMap extends HashMap<String, String> {

    @Override
    public String put(String key, String value) {
       return super.put(key.toLowerCase(), value);
    }

    // not @Override because that would require the key parameter to be of type Object
    public String get(String key) {
       return super.get(key.toLowerCase());
    }
}

要么

http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/map/CaseInsensitiveMap.html


一种方法是创建Apache Commons AbstractHashedMap类的自定义子类,重写hashisEqualKeys方法以执行不区分大小写的散列和键的比较。 (注意 - 我自己从未尝试过这个...)

这样可以避免每次需要进行地图查找或更新时创建新对象的开销。常见的Map操作应该是O(1)......就像常规HashMap一样。

如果您准备接受他们所做的实现选择,Apache Commons CaseInsensitiveMap会为您完成定制/专门化AbstractHashedMap的工作。

但是如果O(logN)getput操作是可接受的,则带有不区分大小写的字符串比较器的TreeMap是一个选项;例如使用String.CASE_INSENSITIVE_ORDER

如果您不介意每次执行putget时都创建一个新的临时String对象,那么Vishal的答案就可以了。 (虽然,我注意到如果你这样做,你就不会保留钥匙的原始情况......)


子类HashMap并创建一个版本,用于降低putget上的键(可能还有其他面向键的方法)。

或者将HashMap复合到新类中并将所有内容委托给地图,但转换键。

如果您需要保留原始密钥,则可以维护双映射,也可以将原始密钥与值一起存储。


我想到了两个选择:

  • 您可以直接使用s.toUpperCase().hashCode();作为Map的键。
  • 您可以使用带有自定义ComparatorTreeMap来忽略大小写。
  • 否则,如果您更喜欢您的解决方案,而不是定义一种新的String,我宁愿实现一个具有所需的不区分大小写功能的新Map。


    您可以使用Eclipse Collections中基于HashingStrategy的Map

    1
    2
    3
    HashingStrategy<String> hashingStrategy =
        HashingStrategies.fromFunction(String::toUpperCase);
    MutableMap<String, String> node = HashingStrategyMaps.mutable.of(hashingStrategy);

    注意:我是Eclipse Collections的贡献者。


    为了记住hashCode,"包装"String不是更好吗?在普通的String类中,hashCode()第一次是O(N),然后它是O(1),因为它被保留以备将来使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class HashWrap {
        private final String value;
        private final int hash;

        public String get() {
            return value;
        }

        public HashWrap(String value) {
            this.value = value;
            String lc = value.toLowerCase();
            this.hash = lc.hashCode();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof HashWrap) {
                HashWrap that = (HashWrap) o;
                return value.equalsIgnoreCase(that.value);
            } else {
                return false;
            }
        }

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

        //might want to implement compare too if you want to use with SortedMaps/Sets.
    }

    这将允许您在java中使用Hashtable的任何实现,并具有O(1)hasCode()。


    有关强大的CaseInsensitiveMap / CaseInsensitiveSet实现,请查看java-util(https://github.com/jdereg/java-util)。

    这些映射在标准O(1)查找时间内执行,保留所添加项的大小写,支持所有Map API,如putAll(),retainAll(),removeAll(),并允许将异构项放入密钥集。

    此外,.keySet()和.entrySet()返回的java.util.Set表示不区分大小写(许多实现没有)。最后,如果在迭代时从键/条目集中获取键,则会返回String,而不是CaseInsensitiveString包装类。


    基于其他答案,基本上有两种方法:子类化HashMap或包装String。第一个需要更多的工作。实际上,如果要正确执行,则必须覆盖几乎所有方法(containsKey, entrySet, get, put, putAll and remove)。

    无论如何,它有一个问题。如果要避免将来出现问题,则必须在String案例操作中指定Locale。所以你会创建新的方法(get(String, Locale),...)。一切都更容易和更清晰包装字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public final class CaseInsensitiveString {

        private final String s;

        public CaseInsensitiveString(String s, Locale locale) {
            this.s = s.toUpperCase(locale);
        }

        // equals, hashCode & toString, no need for memoizing hashCode
    }

    好吧,关于你对性能的担忧:过早的优化是所有邪恶的根源:)


    Because of this, I'm creating a new object of CaseInsensitiveString for every event. So, it might hit performance.

    在查找之前创建包装器或将键转换为小写创建新对象。编写自己的java.util.Map实现是避免这种情况的唯一方法。这不是太难,IMO值得。我发现以下哈希函数工作得很好,最多几百个键。

    1
    2
    3
    4
    5
    static int ciHashCode(String string)
    {
        // length and the low 5 bits of hashCode() are case insensitive
        return (string.hashCode() & 0x1f)*33 + string.length();
    }

    这是我为最近的项目实现的HashMaps的适配器。以类似于@SandyR的方式工作,但封装了转换逻辑,因此您不会手动将字符串转换为包装器对象。

    我使用了Java 8功能,但只需进行一些更改,您就可以将其改编为以前的版本。我测试了大多数常见场景,除了新的Java 8流功能。

    基本上它包装了一个HashMap,在将字符串转换为包装器对象或从包装器对象转换字符串时将所有函数指向它。但我还必须调整KeySet和EntrySet,因为它们将一些函数转发给地图本身。所以我为键和条目返回两个新的集合,它实际上包装了原始的keySet()和entrySet()。

    一个注意事项:Java 8改变了putAll方法的实现,我找不到一种简单的覆盖方法。因此,当前的实现可能会降低性能,尤其是对大型数据集使用putAll()时。

    如果您发现错误或有改进代码的建议,请告诉我。

    package webbit.collections;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    import java.util.*;
    import java.util.function.*;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    import java.util.stream.StreamSupport;


    public class CaseInsensitiveMapAdapter< T > implements Map<String,T>
    {
        private Map<CaseInsensitiveMapKey,T> map;
        private KeySet keySet;
        private EntrySet entrySet;


        public CaseInsensitiveMapAdapter()
        {
        }

        public CaseInsensitiveMapAdapter(Map<String, T> map)
        {
            this.map = getMapImplementation();
            this.putAll(map);
        }

        @Override
        public int size()
        {
            return getMap().size();
        }

        @Override
        public boolean isEmpty()
        {
            return getMap().isEmpty();
        }

        @Override
        public boolean containsKey(Object key)
        {
            return getMap().containsKey(lookupKey(key));
        }

        @Override
        public boolean containsValue(Object value)
        {
            return getMap().containsValue(value);
        }

        @Override
        public T get(Object key)
        {
            return getMap().get(lookupKey(key));
        }

        @Override
        public T put(String key, T value)
        {
            return getMap().put(lookupKey(key), value);
        }

        @Override
        public T remove(Object key)
        {
            return getMap().remove(lookupKey(key));
        }

        /***
         * I completely ignore Java 8 implementation and put one by one.This will be slower.
         */

        @Override
        public void putAll(Map<? extends String, ? extends T> m)
        {
            for (String key : m.keySet()) {
                getMap().put(lookupKey(key),m.get(key));
            }
        }

        @Override
        public void clear()
        {
            getMap().clear();
        }

        @Override
        public Set<String> keySet()
        {
            if (keySet == null)
                keySet = new KeySet(getMap().keySet());
            return keySet;
        }

        @Override
        public Collection< T > values()
        {
            return getMap().values();
        }

        @Override
        public Set<Entry<String, T>> entrySet()
        {
            if (entrySet == null)
                entrySet = new EntrySet(getMap().entrySet());
            return entrySet;
        }

        @Override
        public boolean equals(Object o)
        {
            return getMap().equals(o);
        }

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

        @Override
        public T getOrDefault(Object key, T defaultValue)
        {
            return getMap().getOrDefault(lookupKey(key), defaultValue);
        }

        @Override
        public void forEach(final BiConsumer<? super String, ? super T> action)
        {
            getMap().forEach(new BiConsumer<CaseInsensitiveMapKey, T>()
            {
                @Override
                public void accept(CaseInsensitiveMapKey lookupKey, T t)
                {
                    action.accept(lookupKey.key,t);
                }
            });
        }

        @Override
        public void replaceAll(final BiFunction<? super String, ? super T, ? extends T> function)
        {
            getMap().replaceAll(new BiFunction<CaseInsensitiveMapKey, T, T>()
            {
                @Override
                public T apply(CaseInsensitiveMapKey lookupKey, T t)
                {
                    return function.apply(lookupKey.key,t);
                }
            });
        }

        @Override
        public T putIfAbsent(String key, T value)
        {
            return getMap().putIfAbsent(lookupKey(key), value);
        }

        @Override
        public boolean remove(Object key, Object value)
        {
            return getMap().remove(lookupKey(key), value);
        }

        @Override
        public boolean replace(String key, T oldValue, T newValue)
        {
            return getMap().replace(lookupKey(key), oldValue, newValue);
        }

        @Override
        public T replace(String key, T value)
        {
            return getMap().replace(lookupKey(key), value);
        }

        @Override
        public T computeIfAbsent(String key, final Function<? super String, ? extends T> mappingFunction)
        {
            return getMap().computeIfAbsent(lookupKey(key), new Function<CaseInsensitiveMapKey, T>()
            {
                @Override
                public T apply(CaseInsensitiveMapKey lookupKey)
                {
                    return mappingFunction.apply(lookupKey.key);
                }
            });
        }

        @Override
        public T computeIfPresent(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
        {
            return getMap().computeIfPresent(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
            {
                @Override
                public T apply(CaseInsensitiveMapKey lookupKey, T t)
                {
                    return remappingFunction.apply(lookupKey.key, t);
                }
            });
        }

        @Override
        public T compute(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
        {
            return getMap().compute(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
            {
                @Override
                public T apply(CaseInsensitiveMapKey lookupKey, T t)
                {
                    return remappingFunction.apply(lookupKey.key,t);
                }
            });
        }

        @Override
        public T merge(String key, T value, BiFunction<? super T, ? super T, ? extends T> remappingFunction)
        {
            return getMap().merge(lookupKey(key), value, remappingFunction);
        }

        protected  Map<CaseInsensitiveMapKey,T> getMapImplementation() {
            return new HashMap<>();
        }

        private Map<CaseInsensitiveMapKey,T> getMap() {
            if (map == null)
                map = getMapImplementation();
            return map;
        }

        private CaseInsensitiveMapKey lookupKey(Object key)
        {
            return new CaseInsensitiveMapKey((String)key);
        }

        public class CaseInsensitiveMapKey {
            private String key;
            private String lookupKey;

            public CaseInsensitiveMapKey(String key)
            {
                this.key = key;
                this.lookupKey = key.toUpperCase();
            }

            @Override
            public boolean equals(Object o)
            {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;

                CaseInsensitiveMapKey that = (CaseInsensitiveMapKey) o;

                return lookupKey.equals(that.lookupKey);

            }

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

        private class KeySet implements Set<String> {

            private Set<CaseInsensitiveMapKey> wrapped;

            public KeySet(Set<CaseInsensitiveMapKey> wrapped)
            {
                this.wrapped = wrapped;
            }


            private List<String> keyList() {
                return stream().collect(Collectors.toList());
            }

            private Collection<CaseInsensitiveMapKey> mapCollection(Collection< ? > c) {
                return c.stream().map(it -> lookupKey(it)).collect(Collectors.toList());
            }

            @Override
            public int size()
            {
                return wrapped.size();
            }

            @Override
            public boolean isEmpty()
            {
                return wrapped.isEmpty();
            }

            @Override
            public boolean contains(Object o)
            {
                return wrapped.contains(lookupKey(o));
            }

            @Override
            public Iterator<String> iterator()
            {
                return keyList().iterator();
            }

            @Override
            public Object[] toArray()
            {
                return keyList().toArray();
            }

            @Override
            public < T > T[] toArray(T[] a)
            {
                return keyList().toArray(a);
            }

            @Override
            public boolean add(String s)
            {
                return wrapped.add(lookupKey(s));
            }

            @Override
            public boolean remove(Object o)
            {
                return wrapped.remove(lookupKey(o));
            }

            @Override
            public boolean containsAll(Collection< ? > c)
            {
                return keyList().containsAll(c);
            }

            @Override
            public boolean addAll(Collection<? extends String> c)
            {
                return wrapped.addAll(mapCollection(c));
            }

            @Override
            public boolean retainAll(Collection< ? > c)
            {
                return wrapped.retainAll(mapCollection(c));
            }

            @Override
            public boolean removeAll(Collection< ? > c)
            {
                return wrapped.removeAll(mapCollection(c));
            }

            @Override
            public void clear()
            {
                wrapped.clear();
            }

            @Override
            public boolean equals(Object o)
            {
                return wrapped.equals(lookupKey(o));
            }

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

            @Override
            public Spliterator<String> spliterator()
            {
                return keyList().spliterator();
            }

            @Override
            public boolean removeIf(Predicate<? super String> filter)
            {
                return wrapped.removeIf(new Predicate<CaseInsensitiveMapKey>()
                {
                    @Override
                    public boolean test(CaseInsensitiveMapKey lookupKey)
                    {
                        return filter.test(lookupKey.key);
                    }
                });
            }

            @Override
            public Stream<String> stream()
            {
                return wrapped.stream().map(it -> it.key);
            }

            @Override
            public Stream<String> parallelStream()
            {
                return wrapped.stream().map(it -> it.key).parallel();
            }

            @Override
            public void forEach(Consumer<? super String> action)
            {
                wrapped.forEach(new Consumer<CaseInsensitiveMapKey>()
                {
                    @Override
                    public void accept(CaseInsensitiveMapKey lookupKey)
                    {
                        action.accept(lookupKey.key);
                    }
                });
            }
        }

        private class EntrySet implements Set<Map.Entry<String,T>> {

            private Set<Entry<CaseInsensitiveMapKey,T>> wrapped;

            public EntrySet(Set<Entry<CaseInsensitiveMapKey,T>> wrapped)
            {
                this.wrapped = wrapped;
            }


            private List<Map.Entry<String,T>> keyList() {
                return stream().collect(Collectors.toList());
            }

            private Collection<Entry<CaseInsensitiveMapKey,T>> mapCollection(Collection< ? > c) {
                return c.stream().map(it -> new CaseInsensitiveEntryAdapter((Entry<String,T>)it)).collect(Collectors.toList());
            }

            @Override
            public int size()
            {
                return wrapped.size();
            }

            @Override
            public boolean isEmpty()
            {
                return wrapped.isEmpty();
            }

            @Override
            public boolean contains(Object o)
            {
                return wrapped.contains(lookupKey(o));
            }

            @Override
            public Iterator<Map.Entry<String,T>> iterator()
            {
                return keyList().iterator();
            }

            @Override
            public Object[] toArray()
            {
                return keyList().toArray();
            }

            @Override
            public < T > T[] toArray(T[] a)
            {
                return keyList().toArray(a);
            }

            @Override
            public boolean add(Entry<String,T> s)
            {
                return wrapped.add(null );
            }

            @Override
            public boolean remove(Object o)
            {
                return wrapped.remove(lookupKey(o));
            }

            @Override
            public boolean containsAll(Collection< ? > c)
            {
                return keyList().containsAll(c);
            }

            @Override
            public boolean addAll(Collection<? extends Entry<String,T>> c)
            {
                return wrapped.addAll(mapCollection(c));
            }

            @Override
            public boolean retainAll(Collection< ? > c)
            {
                return wrapped.retainAll(mapCollection(c));
            }

            @Override
            public boolean removeAll(Collection< ? > c)
            {
                return wrapped.removeAll(mapCollection(c));
            }

            @Override
            public void clear()
            {
                wrapped.clear();
            }

            @Override
            public boolean equals(Object o)
            {
                return wrapped.equals(lookupKey(o));
            }

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

            @Override
            public Spliterator<Entry<String,T>> spliterator()
            {
                return keyList().spliterator();
            }

            @Override
            public boolean removeIf(Predicate<? super Entry<String, T>> filter)
            {
                return wrapped.removeIf(new Predicate<Entry<CaseInsensitiveMapKey, T>>()
                {
                    @Override
                    public boolean test(Entry<CaseInsensitiveMapKey, T> entry)
                    {
                        return filter.test(new FromCaseInsensitiveEntryAdapter(entry));
                    }
                });
            }

            @Override
            public Stream<Entry<String,T>> stream()
            {
                return wrapped.stream().map(it -> new Entry<String, T>()
                {
                    @Override
                    public String getKey()
                    {
                        return it.getKey().key;
                    }

                    @Override
                    public T getValue()
                    {
                        return it.getValue();
                    }

                    @Override
                    public T setValue(T value)
                    {
                        return it.setValue(value);
                    }
                });
            }

            @Override
            public Stream<Map.Entry<String,T>> parallelStream()
            {
                return StreamSupport.stream(spliterator(), true);
            }

            @Override
            public void forEach(Consumer<? super Entry<String, T>> action)
            {
                wrapped.forEach(new Consumer<Entry<CaseInsensitiveMapKey, T>>()
                {
                    @Override
                    public void accept(Entry<CaseInsensitiveMapKey, T> entry)
                    {
                        action.accept(new FromCaseInsensitiveEntryAdapter(entry));
                    }
                });
            }
        }

        private class EntryAdapter implements Map.Entry<String,T> {
            private Entry<String,T> wrapped;

            public EntryAdapter(Entry<String, T> wrapped)
            {
                this.wrapped = wrapped;
            }

            @Override
            public String getKey()
            {
                return wrapped.getKey();
            }

            @Override
            public T getValue()
            {
                return wrapped.getValue();
            }

            @Override
            public T setValue(T value)
            {
                return wrapped.setValue(value);
            }

            @Override
            public boolean equals(Object o)
            {
                return wrapped.equals(o);
            }

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


        }

        private class CaseInsensitiveEntryAdapter implements Map.Entry<CaseInsensitiveMapKey,T> {

            private Entry<String,T> wrapped;

            public CaseInsensitiveEntryAdapter(Entry<String, T> wrapped)
            {
                this.wrapped = wrapped;
            }

            @Override
            public CaseInsensitiveMapKey getKey()
            {
                return lookupKey(wrapped.getKey());
            }

            @Override
            public T getValue()
            {
                return wrapped.getValue();
            }

            @Override
            public T setValue(T value)
            {
                return wrapped.setValue(value);
            }
        }

        private class FromCaseInsensitiveEntryAdapter implements Map.Entry<String,T> {

            private Entry<CaseInsensitiveMapKey,T> wrapped;

            public FromCaseInsensitiveEntryAdapter(Entry<CaseInsensitiveMapKey, T> wrapped)
            {
                this.wrapped = wrapped;
            }

            @Override
            public String getKey()
            {
                return wrapped.getKey().key;
            }

            @Override
            public T getValue()
            {
                return wrapped.getValue();
            }

            @Override
            public T setValue(T value)
            {
                return wrapped.setValue(value);
            }
        }


    }


    如何使用java 8流。

    1
    nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())