HashMap และ int เป็นกุญแจสำคัญ


105

ฉันกำลังพยายามสร้าง HashMap ซึ่งจะมีจำนวนเต็มเป็นคีย์และวัตถุเป็นค่า

ไวยากรณ์ของฉันคือ:

HashMap<int, myObject> myMap = new HashMap<int, myObject>();

อย่างไรก็ตามข้อผิดพลาดที่ส่งกลับมาคือ - ข้อผิดพลาดทางไวยากรณ์บนโทเค็น "int", ขนาดที่คาดไว้หลังจากโทเค็นนี้ - ฉันไม่เข้าใจว่าทำไมฉันจึงควรเพิ่มมิติข้อมูล (เช่นการทำให้ int เป็นอาร์เรย์) เนื่องจากฉันต้องเก็บตัวเลขเท่านั้น เป็นกุญแจสำคัญ

ฉันจะทำอย่างไร

ขอบคุณล่วงหน้า! :)


14
HashMapไม่ได้จัดการกับสิ่งดั้งเดิมเป็นเพียงวัตถุ
Menno

คำถาม SO ที่เกี่ยวข้องแต่intเป็นค่าไม่ใช่คีย์
cyroxx

5
ใช้Integerแทน
Hot Licks

autobox int to Integer ดีกว่าไหมหรือแค่เก็บข้อมูลเป็น String แบบไหนสบายกว่ากัน?
Marcin Erbel

คำตอบ:


25

คุณไม่สามารถใช้แบบดั้งเดิมได้เนื่องจาก HashMap ใช้ออบเจ็กต์ภายในสำหรับคีย์ ดังนั้นคุณสามารถใช้วัตถุที่สืบทอดมาจาก Object เท่านั้น (นั่นคือวัตถุใด ๆ )

นั่นคือฟังก์ชันใส่ () ใน HashMap และอย่างที่คุณเห็นมันใช้ Object สำหรับ K:

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

นิพจน์ "k = e.key" ควรทำให้ชัดเจน

ฉันแนะนำให้ใช้ Wrapper เช่น Integer และ autoboxing


139

ใช้Integerแทน

HashMap<Integer, MyObject> myMap = new HashMap<Integer, MyObject>();

Java จะทำให้intค่าดั้งเดิมของคุณเป็นIntegerวัตถุโดยอัตโนมัติ

อ่านเพิ่มเติมเกี่ยวกับการล็อกอัตโนมัติจากเอกสาร Oracle Java


10
เขาไม่ควรตั้งชื่อชั้นเรียนด้วยmyObject
Adam Gent

@AdamGent ถูกต้อง. ชื่อชั้นเรียนทั้งหมดควรขึ้นต้นด้วยตัวพิมพ์ใหญ่ฉันแก้ไขรหัสที่แนะนำ
gaborsch

3
ฉันรู้ว่าคุณรู้ :) ฉันแค่อยากให้แน่ใจว่า OP รู้ / เรียนรู้ สำหรับสิ่งที่ฉันรู้ว่าเขาสามารถใส่ชื่อตัวแปรในพารามิเตอร์ type ได้
Adam Gent

1
เพียงบันทึกสั้น ๆ จะดีกว่าถ้าใช้ArrayMapหรือSimpleArrayMapบน Android เพื่อประหยัดหน่วยความจำและเพิ่มประสิทธิภาพ ( ข้อมูลเพิ่มเติม )
Noah Huppert

42

สำหรับทุกคนที่เขียนโค้ด Java สำหรับอุปกรณ์ Android และลงเอยที่นี่: ใช้SparseArrayเพื่อประสิทธิภาพที่ดีขึ้น

private final SparseArray<myObject> myMap = new SparseArray<myObject>();

ด้วยสิ่งนี้คุณสามารถใช้ int แทนจำนวนเต็มเช่น;

int newPos = 3;

myMap.put(newPos, newObject);
myMap.get(newPos);

7
โปรดจำไว้ว่า SparseArray ช้ากว่า hashmap แต่หน่วยความจำมีประสิทธิภาพมากกว่า ดังนั้นอย่าใช้กับชุดข้อมูลขนาดใหญ่
TpoM6oH

SparseArray ใช้อย่างไรเพื่อประสิทธิภาพที่ดีขึ้นในขณะที่ทำงานช้าลง จะใช้อันไหนในเกม Android ของฉัน
Snake

@Snake SparseArrayหากคุณจัดสรรหน่วยความจำจำนวนมากและ unboxing ints ตามที่คุณทำกับ a HashMapvm จะต้องหยุดการดำเนินการชั่วคราวสำหรับการรวบรวมขยะโดยเร็ว นี่เป็นสิ่งสำคัญหากคุณพยายามทำอะไรบ่อยๆและรวดเร็ว
จอน

1
จำไว้ว่าความซับซ้อนของการแทรกเข้าไปSparseArrayคือO (n) ( HashMapมีO (1) ) เป็นสิ่งสำคัญเมื่อองค์ประกอบมีจำนวนมาก การแทรกในจุดเริ่มต้นของอาร์เรย์นั้นช้ากว่ามาก
Vladimir Petrakovich

1
@ M.kazemAkhgary ไม่เป๊ะ. put()ใช้เวลาO(n)(ไม่n log n) สำหรับการแทรกเมื่อเริ่มต้นเนื่องจากพบตำแหน่งแล้วเลื่อนองค์ประกอบต่อไปนี้ทั้งหมด delete()ตัวเองใช้เวลาแน่นอนO(log n)แต่การแทรกถัดไปหรือ iterating O(n)ผ่านองค์ประกอบหลังจากลบจะต้องมีการเก็บขยะที่ใช้เวลา
Vladimir Petrakovich


4

เหตุผลหลักที่HashMapไม่อนุญาตให้ใช้แบบดั้งเดิมเป็นคีย์ก็คือ HashMap ได้รับการออกแบบมาเพื่อเปรียบเทียบคีย์โดยใช้ค่าเท่ากับ ()วิธีการและสามารถเรียกใช้เมธอดได้เฉพาะบนวัตถุที่ไม่ได้อยู่บนวัตถุดั้งเดิม

ดังนั้นเมื่อ int ถูกทำให้อัตโนมัติเป็นจำนวนเต็ม Hashmap สามารถเรียกใช้เมธอดequals ()บนวัตถุ Integer

นั่นคือเหตุผลที่คุณควรใช้ Integer แทน int ฉันหมายถึงแฮชแมปแสดงข้อผิดพลาดขณะใส่ int เป็นคีย์ (ไม่ทราบความหมายของข้อผิดพลาดที่ถูกโยน)

และถ้าคุณคิดอย่างนั้นคุณสามารถทำให้ประสิทธิภาพของแผนที่เร็วขึ้นได้ด้วยการทำให้ดั้งเดิมเป็นคีย์มีไลบรารีที่เรียกว่าFastUtilซึ่งมีการใช้งานแผนที่โดยมีประเภท int เป็นคีย์

ด้วยเหตุนี้จึงเร็วกว่าHashmap มาก


1
ไม่เหตุผลหลักในการไม่อนุญาตประเภทดั้งเดิมคือการลบประเภทใน Java ซึ่งจะเปลี่ยนMap<Integer, String>เป็นMap<Object, Object>ระหว่างการคอมไพล์ได้อย่างมีประสิทธิภาพ BTW มี IdentityHashMap ที่ใช้==ตัวดำเนินการสำหรับการตรวจสอบความเท่าเทียมกันซึ่งยังไม่อนุญาตประเภทดั้งเดิม
Yoory N.

3

HashMap ไม่อนุญาตให้ประเภทข้อมูลดั้งเดิมเป็นอาร์กิวเมนต์ มันสามารถรับวัตถุได้เท่านั้น

HashMap<int, myObject> myMap = new HashMap<int, myObject>();

จะไม่ทำงาน.

คุณต้องเปลี่ยนคำประกาศเป็น

HashMap<Integer, myObject> myMap = new HashMap<Integer, myObject>();

ดังนั้นแม้ว่าคุณจะทำสิ่งต่อไปนี้

myMap.put(2,myObject);

ชนิดข้อมูลดั้งเดิมจะถูกล็อกอัตโนมัติไปยังอ็อบเจ็กต์ Integer

8 (int) === boxing ===> 8 (Integer)

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการทำ autoboxing ได้ที่นี่http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html



1

ใช้ int เป็น Object ไม่ใช่ประเภทดั้งเดิม

HashMap<Integer, myObject> myMap = new HashMap<Integer, myObject>();

ฉันเขียน HashMap <Integer, MyObject> myMap = HashMap ใหม่ <Integer, MyObject> (); แต่การแสดงทำให้ฉันมีปัญหากับ> และ <เพื่อแสดงคำตอบที่ดี
franki3xe

ฉันรู้ว่ามันเจ็บปวดที่จะพิมพ์ -1แต่ผมพยายามที่จะช่วยให้คุณประหยัดจากทันที ฉันไม่เหมือนคนอื่นแสดงความคิดเห็นก่อนที่จะลงโทษ (ฉันไม่ได้ -1 คุณ)
Adam Gent


0

ฉันไม่เข้าใจว่าทำไมฉันจึงควรเพิ่มมิติ (เช่นการสร้าง int ให้เป็นอาร์เรย์) เนื่องจากฉันต้องเก็บตัวเลขไว้เป็นคีย์เท่านั้น

อาร์เรย์เป็นวัตถุเช่นกันดังนั้นจึงHashMap<int[], MyObject>เป็นโครงสร้างที่ถูกต้องซึ่งใช้อาร์เรย์ภายในเป็นคีย์

คอมไพเลอร์ไม่ทราบว่าคุณต้องการอะไรหรือต้องการอะไรเพียงแค่เห็นโครงสร้างภาษาที่เกือบถูกต้องและเตือนสิ่งที่ขาดหายไปเพื่อให้ถูกต้องทั้งหมด


1
โปรดระวังเรื่องนี้ค่าแฮชของอาร์เรย์ไม่เกี่ยวข้องกับเนื้อหาดังนั้นอาร์เรย์สองอาร์เรย์ที่มีเนื้อหาเดียวกันจึงสามารถแฮชเป็นค่าที่แตกต่างกันได้ทำให้เป็นคีย์ที่ไม่ดีมาก
john16384

0

สำหรับคนที่มีความสนใจในแผนที่ดังกล่าวเพราะคุณต้องการที่จะลดการปล่อยก๊าซของ Autoboxingในชวาห่อมากกว่าประเภทวิทยาการผมจะแนะนำให้ใช้ คอลเลกชันคราส Trove ไม่ได้รับการสนับสนุนอีกต่อไปและฉันเชื่อว่ามันเป็นไลบรารีที่ไม่น่าเชื่อถือเลยทีเดียว (แม้ว่าจะเป็นที่นิยมมากก็ตาม) และไม่สามารถเปรียบเทียบกับคอลเลกชัน Eclipseได้

import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap;

public class Check {
    public static void main(String[] args) {
        IntObjectHashMap map = new IntObjectHashMap();

        map.put(5,"It works");
        map.put(6,"without");
        map.put(7,"boxing!");

        System.out.println(map.get(5));
        System.out.println(map.get(6));
        System.out.println(map.get(7));
    }
}

ในตัวอย่างนี้ด้านบนIntObjectHashMap IntObjectHashMap

ตามที่คุณต้องการการแม็ปวัตถุ int->ให้พิจารณาการใช้YourObjectType[]อาร์เรย์หรือList<YourObjectType>และการเข้าถึงค่าตามดัชนีเช่นเดียวกับแผนที่โดยธรรมชาติแล้วอาร์เรย์ที่เชื่อมโยงที่มีประเภท int เป็นดัชนี

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