Chuỗi không nhạy cảm như khóa HashMap


178

Tôi muốn sử dụng chuỗi không phân biệt chữ hoa chữ thường làm khóa HashMap vì những lý do sau.

  • Trong quá trình khởi tạo, chương trình của tôi tạo HashMap với Chuỗi do người dùng xác định
  • Khi xử lý một sự kiện (lưu lượng truy cập mạng trong trường hợp của tôi), tôi có thể nhận được Chuỗi trong một trường hợp khác nhưng tôi có thể xác định vị trí <key, value>từ HashMap bỏ qua trường hợp tôi nhận được từ lưu lượng truy cập.

Tôi đã theo phương pháp này

CaseInsensitiveString.java

    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;
            }
    }

Tra cứuCode.java

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

Vì điều này, tôi đang tạo một đối tượng mới của CaseInsensitiveString cho mọi sự kiện. Vì vậy, nó có thể đạt hiệu suất.

Có cách nào khác để giải quyết vấn đề này?


3
[Có cách nào tốt để có Bản đồ <Chuỗi ,?> Nhận và đặt trường hợp bỏ qua không?] [1] [1]: stackoverflow.com/questions/212562/ Lỗi
Beau Grantham

Tôi đã nhận xét về các vấn đề bên dưới, nhưng chúng ở dưới ngưỡng để mọi người có thể không nhìn thấy chúng. Cẩn thận với phân lớp HashMap. JDK8 đã thay đổi việc triển khai và bây giờ bạn cần ghi đè putAll (ít nhất) để các đề xuất đó hoạt động.
Steve N

Điều này sẽ làm việc tốt. Bạn có thể sử dụng một con ruồi để thoát khỏi việc khởi tạo đối tượng mới.
topkara

Câu trả lời:


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

Đó thực sự là tất cả những gì bạn cần.


6
Đây là cách đơn giản nhất cho đến nay và cũng bảo tồn trường hợp của các phím khi lặp qua chúng.
Ralf

Thật là đẹp Đây là mảnh ghép cuối cùng để tạo ra một cấu trúc có trật tự trong ColdFusion giúp duy trì khả năng sử dụng ký hiệu dấu chấm. Thay vì var struct = {} hoặc var struct = structnew (), bạn có thể sử dụng var struct = createObject ('java', 'java.util.TreeMap'). Init (createdObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); NHANH CHÓNG, nhưng nó hoạt động;)
Eric Fuller

public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
vui lòng 2/2/2015

5
Không cần <K extends String>kể từ Stringcuối cùng: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker

19
Hãy nhớ rằng TreeMap không phải là thời gian liên tục cho các hoạt động cơ bản. Không phải là một vấn đề cho hầu hết các ứng dụng, nhưng đáng lưu ý. Từ JavaDoc: "Việc triển khai này cung cấp chi phí thời gian của log (n) được bảo đảm cho các khóa chứa, khóa, đặt và xóa. Các thuật toán là các điều chỉnh của các thuật toán trong Cormen, Leiserson và Giới thiệu về thuật toán của Rivest."
James Schek 14/2/2015

57

Theo đề xuất của Guido García trong câu trả lời của họ ở đây :

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());
    }
}

Hoặc là

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html


28
Làm thế nào về chứa, put ALL, vv?
assylias

14
Điều này không hoạt động trong một số ngôn ngữ, như tiếng Thổ Nhĩ Kỳ. Google "Thử nghiệm gà tây"
Hugo

5
@assylias: đúng, containsKey()remove()nên được ghi đè theo cùng một cách như get(). việc HashMap.putAll()triển khai sử dụng put(), do đó không phải là một vấn đề - miễn là việc triển khai HashMap giữ nguyên. ;) cũng là get()chữ ký phương thức lấy một Objectđối số, không phải là a String. mã này cũng không kiểm tra khóa null:super.get(key == null ? null : key.toString().toLowercase());
sfera

lưu ý rằng nếu bạn yêu cầu trình tạo bản sao HashMap(<? extends String, ? extends String> anotherMap), thì bạn không nên gọi siêu triển khai của cùng một hàm tạo vì thao tác đó sẽ không đảm bảo rằng các khóa của bạn là chữ thường. bạn có thể sử dụng: super(anotherMap.size()); putAll(anotherMap);thay vào đó.
sfera

Điều gì xảy ra nếu bạn muốn các giá trị của bản đồ không phải là chuỗi? (tức là CaseInsensitiveMap<String, Integer>)
Adam Parkin

16

Một cách tiếp cận là tạo một lớp con tùy chỉnh của lớp Apache Commons AbstractHashedMap, ghi đè hashisEqualKeyscác phương thức để thực hiện băm không phân biệt chữ hoa chữ thường và so sánh các khóa. (Lưu ý - Tôi chưa bao giờ thử cái này ...)

Điều này tránh được chi phí tạo các đối tượng mới mỗi khi bạn cần thực hiện tra cứu hoặc cập nhật bản đồ. Và các Maphoạt động chung nên O (1) ... giống như thông thường HashMap.

Và nếu bạn sẵn sàng chấp nhận các lựa chọn triển khai mà họ đã thực hiện, Apache Commons CaseInsensitiveMapthực hiện công việc tùy chỉnh / chuyên biệt AbstractHashedMapcho bạn.


Nhưng nếu O (logN) getvà các puthoạt động có thể chấp nhận được, thì một TreeMapbộ so sánh chuỗi không phân biệt chữ hoa chữ thường là một tùy chọn; ví dụ như sử dụng String.CASE_INSENSITIVE_ORDER.

Và nếu bạn không phiền khi tạo một đối tượng Chuỗi tạm thời mới mỗi khi bạn thực hiện puthoặc get, thì câu trả lời của Vishal là tốt. (Mặc dù, tôi lưu ý rằng bạn sẽ không bảo vệ trường hợp ban đầu của các phím nếu bạn đã làm điều đó ...)


6

Phân lớp HashMapvà tạo một phiên bản viết thường khóa putget(và có lẽ các phương thức định hướng khóa khác).

Hoặc tổng hợp a HashMap vào lớp mới và ủy thác mọi thứ cho bản đồ, nhưng dịch các phím.

Nếu bạn cần giữ khóa gốc, bạn có thể duy trì bản đồ kép hoặc lưu trữ khóa gốc cùng với giá trị.


Bạn có nghĩa là làm một String.toLowerCase () trong quá trình tra cứu?
rs

@ user710178 Không chỉ trong quá trình tra cứu mà cả trong quá trình lưu trữ.
Dave Newton

@ user710178 Ồ, đúng rồi, như câu trả lời khác chỉ ra, điều này đã tồn tại, nếu bạn không ngại phụ thuộc thêm.
Dave Newton

@StephenC Nếu nó đáp ứng nhu cầu của bạn, chắc chắn; OP đã chỉ định a HashMap, vì vậy đó là những gì tôi đã đi với :) Ồ, ý bạn là Commons; Tôi hiểu rồi. Tôi đoán, miễn là bạn không cần nó được tạo ra (hoặc cuối cùng họ có thuốc generic bây giờ không?)
Dave Newton

1
Đối với JDK 8 trở lên, bạn cũng sẽ cần (ít nhất) ghi đè putAll khi việc triển khai đã thay đổi.
Steve N

4

Hai lựa chọn đến với tôi:

  1. Bạn có thể sử dụng trực tiếp s.toUpperCase().hashCode();như là chìa khóa của Map.
  2. Bạn có thể sử dụng một TreeMap<String>với một tùy chỉnh Comparatormà bỏ qua trường hợp.

Mặt khác, nếu bạn thích giải pháp của mình, thay vì xác định một loại Chuỗi mới, tôi thà triển khai Bản đồ mới với chức năng không phân biệt chữ hoa chữ thường.


3

Sẽ không tốt hơn nếu "bọc" Chuỗi để ghi nhớ mã băm. Trong lớp StringCode bình thường () là O (N) lần đầu tiên và sau đó là O (1) vì nó được giữ lại để sử dụng trong tương lai.

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.
}

Điều này sẽ cho phép bạn sử dụng bất kỳ triển khai Hashtable nào trong java và có O (1) hasCode ().


3

Bạn có thể sử dụng một HashingStrategy dựa Maptừ Eclipse Bộ sưu tập

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

Lưu ý: Tôi là người đóng góp cho Bộ sưu tập Eclipse.


2

Dựa trên các câu trả lời khác, về cơ bản có hai cách tiếp cận: phân lớp HashMaphoặc gói String. Việc đầu tiên đòi hỏi một chút công việc. Trong thực tế, nếu bạn muốn làm điều đó một cách chính xác, bạn phải ghi đè gần như tất cả các phương thức (containsKey, entrySet, get, put, putAll and remove ).

Dù sao, nó có một vấn đề. Nếu bạn muốn tránh các vấn đề trong tương lai, bạn phải xác định một Localetrong Stringtrường hợp hoạt động. Vì vậy, bạn sẽ tạo các phương thức mới ( get(String, Locale), ...). Mọi thứ đều dễ dàng hơn và rõ ràng hơn Chuỗi String:

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
}

Và tốt, về những lo lắng của bạn về hiệu suất: tối ưu hóa sớm là gốc rễ của mọi tội lỗi :)


2
"Và tốt, về những lo lắng của bạn về hiệu suất: tối ưu hóa sớm là gốc rễ của mọi tội lỗi :)" - Ngược lại, sử dụng nó như một cái cớ để luôn luôn viết mã không hiệu quả là điều xấu.
Gordon

1
Thật ra @Gordon, cả hai đều tệ như nhau, tùy thuộc vào ngữ cảnh. Nhãn "ác" là một dấu hiệu của suy nghĩ trắng đen, như "thực hành tốt nhất" và nhiều cụm từ không có ích khác mà nhiều người CNTT có xu hướng sử dụng. Tốt nhất để tránh nó hoàn toàn.
Stephen C

Tôi đã nói với mọi người rằng họ không tuân theo "thực hành tốt nhất" có xu hướng tạo ra ít hoạt động đào hơn là nói với họ rằng họ có những thực hành xấu.
Gordon

0

Đây là một bộ chuyển đổi cho HashMaps mà tôi đã triển khai cho một dự án gần đây. Hoạt động theo cách mô phỏng theo những gì @SandyR làm, nhưng đóng gói logic chuyển đổi để bạn không chuyển đổi thủ công chuỗi thành đối tượng trình bao bọc.

Tôi đã sử dụng các tính năng Java 8 nhưng với một vài thay đổi, bạn có thể điều chỉnh nó với các phiên bản trước. Tôi đã thử nghiệm nó cho hầu hết các kịch bản phổ biến, ngoại trừ các hàm luồng Java 8 mới.

Về cơ bản, nó bao bọc một HashMap, chỉ đạo tất cả các chức năng cho nó trong khi chuyển đổi các chuỗi thành / từ một đối tượng trình bao bọc. Nhưng tôi cũng phải điều chỉnh Keyset và Entryset vì chúng chuyển tiếp một số chức năng cho chính bản đồ. Vì vậy, tôi trả lại hai Bộ mới cho các khóa và các mục thực sự bao gồm các khóa ban đầu () và entryset ().

Một lưu ý: Java 8 đã thay đổi việc triển khai phương thức putAll mà tôi không thể tìm thấy một cách dễ dàng để ghi đè. Vì vậy, việc triển khai hiện tại có thể làm giảm hiệu suất, đặc biệt nếu bạn sử dụng putAll () cho một tập dữ liệu lớn.

Xin vui lòng cho tôi biết nếu bạn tìm thấy một lỗi hoặc có đề xuất để cải thiện mã.

gói webbit.collections;

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);
        }
    }


}

0

Vì điều này, tôi đang tạo một đối tượng mới của CaseInsensitiveString cho mọi sự kiện. Vì vậy, nó có thể đạt hiệu suất.

Tạo hàm bao hoặc khóa chuyển đổi sang chữ thường trước khi tra cứu cả hai tạo đối tượng mới. Viết triển khai java.util.Map của riêng bạn là cách duy nhất để tránh điều này. Nó không quá khó, và IMO là xứng đáng. Tôi thấy hàm băm sau hoạt động khá tốt, lên tới vài trăm khóa.

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

-3

Làm thế nào về việc sử dụng java 8 luồng.

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

Điều này không cho phép bạn tra cứu các giá trị trong bản đồ theo cách không phân biệt chữ hoa chữ thường.
Gili

Equalsignorecase sẽ làm điều đó phải không?
Amarendra Reddy

Bạn đang xây dựng một danh sách. OP yêu cầu bản đồ.
Gili

Điều này phá hủy lợi ích phức tạp O (1) của bản đồ.
Paul Rooney
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.