สตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เป็นคีย์ HashMap


178

ฉันต้องการใช้สตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เป็นคีย์ HashMap ด้วยเหตุผลดังต่อไปนี้

  • ในระหว่างการเริ่มต้นโปรแกรมของฉันสร้าง HashMap ด้วย String ที่ผู้ใช้กำหนด
  • ขณะประมวลผลเหตุการณ์ (ปริมาณการใช้เครือข่ายในกรณีของฉัน) ฉันอาจได้รับ String ในกรณีอื่น แต่ฉันควรจะสามารถค้นหา <key, value>จาก HashMap โดยไม่สนใจกรณีที่ฉันได้รับจากปริมาณการใช้งาน

ฉันได้ทำตามวิธีนี้แล้ว

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

LookupCode.java

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

ด้วยเหตุนี้ฉันจึงสร้างวัตถุใหม่ของ CaseInsensitiveString สำหรับทุกเหตุการณ์ ดังนั้นมันอาจกระทบกับประสิทธิภาพการทำงาน

มีวิธีอื่นในการแก้ไขปัญหานี้หรือไม่?


3
[มีวิธีที่ดีในการมีแผนที่ <String,?> รับและวางกรณีที่ไม่สนใจหรือไม่] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham

ฉันได้แสดงความคิดเห็นเกี่ยวกับปัญหาด้านล่าง แต่พวกเขาต่ำกว่าเกณฑ์ดังนั้นผู้คนอาจไม่เห็นพวกเขา ระวังการใช้คลาสย่อย HashMap JDK8 ได้เปลี่ยนการใช้งานและตอนนี้คุณต้องแทนที่ putAll (อย่างน้อย) เพื่อรับข้อเสนอแนะเหล่านั้นในการทำงาน
Steve N

ควรทำงานได้ดี คุณสามารถใช้ฟลายเวทเพื่อกำจัดอินสแตนซ์ของวัตถุใหม่
topkara

คำตอบ:


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

นั่นคือทั้งหมดที่คุณต้องการ


6
นี่คือขั้นตอนที่ง่ายที่สุดและยังเก็บรักษากรณีของปุ่มต่าง ๆ เมื่อทำการวนซ้ำ
Ralf

นี่คือสิ่งที่สวยงาม! นี่คือชิ้นส่วนสุดท้ายของปริศนาสำหรับการสร้าง struct สั่งใน ColdFusion ที่รักษาความสามารถในการใช้เครื่องหมายจุด แทน var struct = {} หรือ var struct = structnew () คุณสามารถใช้ var struct = createObject ('java', 'java.util.TreeMap') init (createObject ('java', 'java.lang.String') ) .CASE_INSENSITIVE_ORDER); FUGLY แต่ใช้งานได้;)
Eric Fuller

public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
pllee

5
ไม่จำเป็นต้องมี<K extends String>ตั้งแต่Stringเป็นที่สิ้นสุด: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker

19
โปรดทราบว่า TreeMap ไม่ใช่เวลาคงที่สำหรับการดำเนินการขั้นพื้นฐาน ไม่ใช่ปัญหาสำหรับแอปพลิเคชันส่วนใหญ่ แต่ควรคำนึงถึง จาก JavaDoc: "การใช้งานนี้มีการรับประกันเวลาบันทึก (n) ค่าใช้จ่ายสำหรับ containKey, รับ, ใส่และลบการดำเนินงานอัลกอริทึมเป็นการดัดแปลงของ Cormen, Leiserson และ Rivest's Introduction to Algorithms"
James Schek

57

ตามคำแนะนำของ Guido Garcíaในคำตอบของพวกเขาที่นี่ :

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

หรือ

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


28
วิธีการเกี่ยวกับประกอบด้วย, putAll, ฯลฯ ?
assylias

14
สิ่งนี้ไม่ทำงานในบางภาษาเช่นภาษาตุรกี Google "การทดสอบไก่งวง"
Hugo

5
@assylias: ความจริงcontainsKey()และควรได้รับการแทนที่ในแบบเดียวกับที่remove() ใช้การดำเนินการเพื่อที่ว่าไม่ควรจะมีปัญหา - ตราบใดที่การเข้าพักดำเนินการ HashMap เดียวกัน ;) นอกจากนี้ยังมีลายเซ็นวิธีใช้เวลาเป็นอาร์กิวเมนต์ไม่ รหัสยังไม่ได้ทดสอบสำหรับคีย์ว่าง:get()HashMap.putAll()put()get()ObjectStringsuper.get(key == null ? null : key.toString().toLowercase());
sfera

โปรดทราบว่าหากคุณต้องการ copy-constructor HashMap(<? extends String, ? extends String> anotherMap)คุณไม่ควรเรียก super implement ของ constructor ตัวเดียวกันเพราะการทำงานนั้นจะไม่รับประกันว่ากุญแจของคุณจะเป็นตัวพิมพ์เล็ก คุณสามารถใช้: super(anotherMap.size()); putAll(anotherMap);แทน
sfera

ถ้าคุณต้องการให้ค่าของแผนที่เป็นสตริง (เช่นCaseInsensitiveMap<String, Integer>)
Adam Parkin

16

วิธีการหนึ่งคือการสร้างคลาสย่อยที่กำหนดเองของคลาส Apache Commons การAbstractHashedMapแทนที่hashและisEqualKeysวิธีการดำเนินการแฮ็บที่ไม่สนใจขนาดตัวพิมพ์และการเปรียบเทียบคีย์ (หมายเหตุ - ฉันไม่เคยลองด้วยตัวเอง ... )

วิธีนี้จะช่วยหลีกเลี่ยงค่าใช้จ่ายในการสร้างวัตถุใหม่ทุกครั้งที่คุณต้องทำการค้นหาแผนที่หรืออัพเดท และร่วมกันMapดำเนินงานควร O (1) ... HashMapเหมือนปกติ

และถ้าคุณพร้อมที่จะยอมรับตัวเลือกการใช้งานที่พวกเขาได้ทำ Apache Commons CaseInsensitiveMapจะทำงานในการปรับแต่ง / เชี่ยวชาญAbstractHashedMapสำหรับคุณ


แต่ถ้า O (logN) getและputการดำเนินงานเป็นที่ยอมรับ a TreeMapด้วยตัวเปรียบเทียบสตริงที่ไม่คำนึงถึงขนาดตัวพิมพ์เป็นตัวเลือก เช่นการใช้String.CASE_INSENSITIVE_ORDERเช่นใช้

และถ้าคุณไม่รังเกียจที่จะสร้างวัตถุ String ชั่วคราวใหม่ทุกครั้งที่คุณทำputหรือgetจากนั้นคำตอบของ Vishal ก็ใช้ได้ (แม้ว่าฉันจะทราบว่าคุณจะไม่ได้รับการรักษากรณีเดิมของปุ่มถ้าคุณทำอย่างนั้น ... )


6

ซับคลาสHashMapและสร้างเวอร์ชันที่ใช้ตัวพิมพ์เล็กputและตัวพิมพ์ใหญ่getพิมพ์ใหญ่

หรือคอมโพสิต HashMapคลาสใหม่และมอบหมายทุกอย่างให้กับแผนที่ แต่แปลคีย์

หากคุณต้องการเก็บรหัสต้นฉบับไว้คุณสามารถบำรุงรักษาแผนที่คู่หรือเก็บรหัสต้นฉบับพร้อมกับค่า


คุณหมายถึงทำ String.toLowerCase () ระหว่างการค้นหาหรือไม่?
rs

@ user710178 ไม่เพียง แต่ในระหว่างการค้นหา แต่ระหว่างการจัดเก็บเช่นกัน
Dave Newton

@ user710178 โอ้ใช่แล้วอย่างที่คำตอบอื่น ๆ ชี้ว่ามีอยู่แล้วถ้าคุณไม่สนใจการพึ่งพาเพิ่มเติม
Dave Newton

@StephenC หากตรงกับความต้องการของคุณแน่นอน; OP ระบุ a HashMap, นั่นคือสิ่งที่ฉันไปด้วย :) โอ้คุณหมายถึงคอมมอนส์; ฉันเห็น. ฉันเดาว่าตราบใดที่คุณไม่ต้องการให้มีการสร้าง (หรือในที่สุดพวกเขาก็มียาชื่อสามัญตอนนี้?)
เดฟนิวตัน

1
สำหรับ JDK 8 ขึ้นไปคุณจะต้องแทนที่ (อย่างน้อย) putAll เมื่อการเปลี่ยนแปลงมีผล
Steve N

4

ทางเลือกสองทางที่อยู่ในใจของฉัน:

  1. คุณสามารถใช้โดยตรงเป็นกุญแจสำคัญของs.toUpperCase().hashCode();Map
  2. คุณสามารถใช้แบบTreeMap<String>กำหนดเองComparatorที่ไม่สนใจเคสได้

มิฉะนั้นหากคุณต้องการโซลูชันของคุณแทนที่จะกำหนด String ชนิดใหม่ฉันควรจะใช้แผนที่ใหม่พร้อมกับฟังก์ชั่นการตรวจสอบขนาดตัวพิมพ์ที่ต้องการ


3

มันจะเป็นการดีกว่าถ้าจะ "ห่อ" สตริงเพื่อจดจำ hashCode ในคลาส String ปกติ hashCode () คือ O (N) ในครั้งแรกและจากนั้นก็จะเป็น O (1) เนื่องจากมันถูกเก็บไว้เพื่อใช้ในอนาคต

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

สิ่งนี้จะทำให้คุณสามารถใช้งาน Hashtable ใน java และมี O (1) hasCode ()



2

ขึ้นอยู่กับคำตอบอื่น ๆ มีพื้นสองวิธี: subclassing หรือห่อHashMap Stringคนแรกต้องทำงานเพิ่มอีกนิด ในความเป็นจริงหากคุณต้องการทำอย่างถูกต้องคุณต้องแทนที่วิธีการเกือบทั้งหมด ( containsKey, entrySet, get, put, putAll and remove)

อย่างไรก็ตามมันมีปัญหา หากคุณต้องการหลีกเลี่ยงปัญหาในอนาคตคุณต้องระบุการดำเนินการLocaleในStringกรณี ดังนั้นคุณจะสร้างวิธีการใหม่ ( get(String, Locale), ... ) ทุกอย่างง่ายและชัดเจนกว่าการตัดสตริง:

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
}

และเกี่ยวกับความกังวลของคุณเกี่ยวกับประสิทธิภาพ: การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด :)


2
"และที่เกี่ยวกับความกังวลของคุณเกี่ยวกับประสิทธิภาพ: การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของความชั่วร้ายทั้งหมด :)" - ในทางกลับกันการใช้สิ่งนี้เป็นข้อแก้ตัวในการเขียนโค้ดที่ไม่มีประสิทธิภาพเสมอคือสิ่งที่ชั่วร้าย
Gordon

1
จริงๆแล้ว @Gordon ทั้งคู่ต่างก็ไม่ดีเท่ากันขึ้นอยู่กับบริบท ป้ายกำกับ "ความชั่วร้าย" เป็นสัญลักษณ์ของการคิดแบบขาวดำเช่น "แนวปฏิบัติที่ดีที่สุด" และวลีที่ไม่มีประโยชน์อื่น ๆ อีกมากมายที่ผู้คนไอทีจำนวนมากมักใช้ ดีที่สุดที่จะหลีกเลี่ยงมันทั้งหมด
Stephen C

ฉันพบว่าคนที่ไม่ปฏิบัติตาม "แนวปฏิบัติที่ดีที่สุด" มีแนวโน้มที่จะผลิตรองเท้าส้นสูงน้อยกว่าบอกพวกเขาว่าพวกเขามีแนวปฏิบัติที่ไม่ดี
Gordon

0

นี่คืออะแดปเตอร์สำหรับ HashMaps ที่ฉันนำมาใช้สำหรับโครงการล่าสุด ทำงานในแบบที่คล้ายกับสิ่งที่ @SandR ทำ แต่จะสรุปตรรกะการแปลงเพื่อให้คุณไม่แปลงสตริงเป็นวัตถุ wrapper ด้วยตนเอง

ฉันใช้คุณสมบัติ Java 8 แต่มีการเปลี่ยนแปลงเล็กน้อยคุณสามารถปรับให้เข้ากับรุ่นก่อนหน้า ฉันทดสอบมันสำหรับสถานการณ์ทั่วไปยกเว้นฟังก์ชั่นการสตรีม Java 8 ใหม่

โดยทั่วไปจะห่อ HashMap นำฟังก์ชั่นทั้งหมดไปในขณะที่การแปลงสตริงไปยัง / จากวัตถุห่อหุ้ม แต่ฉันต้องปรับ KeySet และ EntrySet ด้วยเพราะพวกมันจะส่งต่อฟังก์ชั่นบางอย่างไปยังแผนที่เอง ดังนั้นฉันจึงส่งคืนชุดใหม่สองชุดสำหรับคีย์และรายการซึ่งจริง ๆ แล้วตัดชุด keySet () และ entrySet () จริง ๆ

One note: Java 8 ได้เปลี่ยนวิธีการใช้งานของ putAll ซึ่งฉันไม่สามารถหาวิธีที่ง่ายในการแทนที่ ดังนั้นการนำไปใช้ในปัจจุบันอาจมีประสิทธิภาพลดลงโดยเฉพาะถ้าคุณใช้ putAll () สำหรับชุดข้อมูลขนาดใหญ่

โปรดแจ้งให้เราทราบหากคุณพบข้อบกพร่องหรือมีข้อเสนอแนะในการปรับปรุงรหัส

แพคเกจ 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

ด้วยเหตุนี้ฉันจึงสร้างวัตถุใหม่ของ CaseInsensitiveString สำหรับทุกเหตุการณ์ ดังนั้นมันอาจกระทบกับประสิทธิภาพการทำงาน

การสร้าง wrappers หรือการแปลงคีย์เป็นตัวพิมพ์เล็กก่อนที่จะค้นหาทั้งคู่สร้างวัตถุใหม่ การเขียนการติดตั้ง java.util.Map ของคุณเองเป็นวิธีเดียวที่จะหลีกเลี่ยงปัญหานี้ได้ มันไม่ยากเกินไปและ IMO ก็คุ้มค่า ฉันพบฟังก์ชันแฮชต่อไปนี้ทำงานได้ดีมากมีคีย์ไม่กี่ร้อยตัว

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

-3

วิธีการเกี่ยวกับการใช้ java 8 สตรีม

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

สิ่งนี้ไม่ได้ช่วยให้คุณสามารถค้นหาค่าในแผนที่ในแบบตัวพิมพ์ใหญ่ - เล็ก
Gili

เท่ากับจะทำเช่นนั้นไม่ได้หรือไม่
Amarendra Reddy

คุณกำลังสร้างรายการ OP ขอแผนที่
Gili

สิ่งนี้จะทำลายผลประโยชน์ความซับซ้อนของ O (1) ของแผนที่
พอลรูนีย์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.