การแปลง java.util.Properties เป็น HashMap <String, String>


92
Properties properties = new Properties();
Map<String, String> map = new HashMap<String, String>(properties);// why wrong?

java.util.Propertiesคือการใช้งานjava.util.Mapและตัวjava.util.HashMapสร้างของได้รับMapชนิดพารามิเตอร์ เหตุใดจึงต้องแปลงอย่างโจ่งแจ้ง?

คำตอบ:


88

นี่เป็นเพราะการPropertiesขยายHashtable<Object, Object> (ซึ่งในทางกลับกันการดำเนินการMap<Object, Object>) คุณพยายามป้อนข้อมูลนั้นเป็นไฟล์Map<String, String>. มันจึงเข้ากันไม่ได้

คุณต้องป้อนคุณสมบัติสตริงทีละรายการในแผนที่ของคุณ ...

ตัวอย่างเช่น:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));

1
ใช่ แต่นั่นไม่ใช่ปัญหาที่นี่: อาร์กิวเมนต์ทั่วไปไม่ตรงกัน คุณสามารถป้อนอะไรก็ได้ที่คุณต้องการใน a Hashtable<Object, Object>แม้แต่สิ่งที่ไม่ใช่สตริง - แม้แต่คีย์ที่ไม่ใช่สตริง
fge

@assylias: ไม่นั่นจะไม่รวบรวมเช่นกัน
Jon Skeet

13
ใน 1,8 คุณสามารถทำ properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire

1
หรือถ้าคุณยังไม่มีแผนที่ในมือ properties.entrySet (). stream (). collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic

47

วิธีที่มีประสิทธิภาพในการทำเช่นนั้นก็เพียงแค่ส่งไปยังแผนที่ทั่วไปดังนี้:

Properties props = new Properties();

Map<String, String> map = (Map)props;

สิ่งนี้จะแปลง a Map<Object, Object>เป็นแผนที่ดิบซึ่ง "ตกลง" สำหรับคอมไพเลอร์ (คำเตือนเท่านั้น) เมื่อเราได้ดิบMapมันก็จะร่ายMap<String, String>ซึ่งมันก็จะ "ตกลง" ด้วย (คำเตือนอื่น) คุณสามารถเพิกเฉยได้ด้วยคำอธิบายประกอบ@SuppressWarnings({ "unchecked", "rawtypes" })

สิ่งนี้จะใช้งานได้เนื่องจากใน JVM วัตถุไม่มีประเภททั่วไป ประเภททั่วไปเป็นเพียงเคล็ดลับที่ตรวจสอบสิ่งต่าง ๆ ในเวลารวบรวม

หากคีย์หรือค่าบางอย่างไม่ใช่สตริงจะทำให้เกิดClassCastExceptionข้อผิดพลาด ด้วยปัจจุบันPropertiesการดำเนินงานนี้มากไม่น่าจะเกิดขึ้นได้ตราบใดที่คุณไม่ได้ใช้วิธีการโทรไม่แน่นอนจากซุปเปอร์ของHashtable<Object,Object>Properties

ดังนั้นหากไม่ทำสิ่งที่น่ารังเกียจกับอินสแตนซ์ Properties ของคุณนี่คือวิธีที่จะไป


คำถามคือการแปลงเป็น HashMap ไม่ใช่แผนที่ใด ๆ
AlikElzin-kilaka

3
ใช่ชื่อคำถามบอกอย่างนั้น แต่เป้าหมายคือต้องมีMapอินสแตนซ์อย่างน้อยตามรหัสที่กำหนดดังนั้นฉันจึงคิดว่านี่คือสิ่งที่เขาต้องการ
padilo

แม้ว่าฉันจะชอบวิธีแก้ปัญหาแบบเจ้าระเบียบอื่น ๆ แต่วิธีนี้ก็มีประโยชน์สำหรับฉันเพราะมันเป็นเพียงบรรทัดเดียวเท่านั้น
Alfonso Nishikawa


28

แล้วเรื่องนี้ล่ะ?

   Map properties = new Properties();
   Map<String, String> map = new HashMap<String, String>(properties);

จะทำให้เกิดคำเตือน แต่ใช้งานได้โดยไม่ต้องทำซ้ำ


4
@fge: ไม่ใช่ a Map<Object, Object>แต่เป็นMapอาร์กิวเมนต์ (ประเภทดิบ) คำตอบนี้ถูกต้อง
Lukas Eder

2
ใช่ฉันลองกับ Eclipse แล้ว หนึ่งในความแตกต่างทั่วไประหว่าง Eclipse และ javac อีกครั้ง? .... ไม่ทำงานกับ javac ได้เช่นกัน
Lukas Eder

4
วิธีนี้ใช้ได้ผล แต่การวนซ้ำยังคงเกิดขึ้น หากคุณดูซอร์สโค้ดสำหรับ HashMap ตัวสร้างโดยพื้นฐานแล้วจะวนซ้ำผ่านพารามิเตอร์แผนที่ทั่วไป ดังนั้นเวลาในการคำนวณจึงไม่เปลี่ยนแปลง แต่รหัสจะรัดกุมกว่าอย่างแน่นอน
Simeon G

ตามคำตอบก่อนหน้านี้ไม่จำเป็นต้องสร้างอินสแตนซ์ใหม่และทำซ้ำบนออบเจ็กต์คุณสมบัติ เพียงใช้ลำดับการร่าย: (Map<String, String>) ((Map) properties)
Ricardo Veloso


17

PropertiesการดำเนินการMap<Object, Object>- Map<String, String>ไม่ได้

คุณกำลังพยายามเรียกตัวสร้างนี้ว่า:

public HashMap(Map<? extends K,? extends V> m)

... ด้วยKและVทั้งสองStringอย่าง

แต่Map<Object, Object>ไม่ใช่Map<? extends String, ? extends String>... มันสามารถมีคีย์และค่าที่ไม่ใช่สตริงได้

สิ่งนี้จะได้ผล:

Map<Object, Object> map = new HashMap<Object, Object>();

... แต่มันคงไม่มีประโยชน์สำหรับคุณ

โดยพื้นฐานแล้วPropertiesไม่ควรสร้างคลาสย่อยของHashTable... นั่นคือปัญหา ตั้งแต่ v1 มันสามารถจัดเก็บคีย์และค่าที่ไม่ใช่สตริงได้เสมอแม้ว่าจะขัดต่อเจตนาก็ตาม หากมีการใช้การจัดองค์ประกอบแทน API สามารถใช้ได้เฉพาะกับคีย์ / ค่าสตริงเท่านั้นและทั้งหมดจะเป็นไปด้วยดี

คุณอาจต้องการสิ่งนี้:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}

Properties<String,String> properties = new Properties<String,String>();เห็นได้ชัดว่ามันก็เป็นไปไม่ได้ที่จะทำอย่างชัดเจน แปลก
eis

1
@eis นั่นคือจากการออกแบบPropertiesตัวมันเองไม่ใช่เรื่องธรรมดา
Mattias Buelens

ฉันอยากจะบอกว่ามันเป็นตัวเลือกที่โชคร้ายมากกว่าโดยการออกแบบ แต่ใช่
eis

2
@eis: ไม่มันเกิดจากการออกแบบที่ Properties หมายถึงการแม็พแบบสตริงต่อสตริง ทำให้รู้สึกว่าไม่ใช่เรื่องทั่วไป มันไม่ได้ทำให้รู้สึกว่าคุณสามารถเพิ่มปุ่มไม่ใช่สตริง / ค่า
Jon Skeet


8

หากคุณรู้ว่าPropertiesวัตถุของคุณมีเฉพาะ<String, String>รายการคุณสามารถใช้ประเภทดิบ:

Properties properties = new Properties();
Map<String, String> map = new HashMap<String, String>((Map) properties);

4

ปัญหาคือPropertiesการใช้งานMap<Object, Object>ในขณะที่ตัวHashMapสร้างคาดว่าจะมีMap<? extends String, ? extends String>.

คำตอบนี้อธิบายถึงการตัดสินใจ (ค่อนข้างสวนทางกัน) กล่าวโดยย่อ: ก่อน Java 5 Propertiesนำไปใช้Map(เนื่องจากไม่มีชื่อสามัญในเวลานั้น) นั่นหมายความว่าคุณสามารถใส่อะไรก็ได้ ObjectในPropertiesวัตถุ สิ่งนี้ยังอยู่ในเอกสาร:

เพราะPropertiesสืบทอดจากHashtableที่putและputAllวิธีการที่สามารถนำไปใช้กับPropertiesวัตถุ ใช้ของพวกเขาเป็นกำลังใจอย่างยิ่งที่พวกเขาให้โทรไปแทรกรายการที่มีคีย์หรือค่าไม่ได้Strings setPropertyวิธีการที่ควรจะนำมาใช้แทน

เพื่อรักษาความเข้ากันได้กับสิ่งนี้นักออกแบบจึงไม่มีทางเลือกอื่นนอกจากทำให้มันสืบทอดMap<Object, Object>ใน Java 5 มันเป็นผลลัพธ์ที่น่าเสียดายจากความพยายามในการใช้งานร่วมกันได้อย่างสมบูรณ์แบบย้อนกลับซึ่งทำให้โค้ดใหม่มีความซับซ้อนโดยไม่จำเป็น

หากคุณเคยใช้คุณสมบัติสตริงในPropertiesออบเจ็กต์ของคุณเท่านั้นคุณควรจะสามารถหลีกเลี่ยงการร่ายที่ไม่ได้เลือกในตัวสร้างของคุณ

Map<String, String> map = new HashMap<String, String>( (Map<String, String>) properties);

หรือไม่มีสำเนา:

Map<String, String> map = (Map<String, String>) properties;

นี่คือลายเซ็นของ HashMap public HashMap(Map<? extends K, ? extends V> m)สร้าง ไม่คาดว่า aMap<String, String>
Mubin

@ มูบินโอเคฉันปรับเรื่องเล็กน้อยเกินไป อย่างไรก็ตามอาร์กิวเมนต์ถือ: Map<Object, Object>ไม่สามารถใช้สำหรับอาร์กิวเมนต์ที่เป็นทางการของ type` Map <? ขยาย String,? ขยาย String> ".
Mattias Buelens

2

นี่เป็นเพียงเพราะตัวสร้างของ HashMap ต้องการอาร์กิวเมนต์ของประเภทแผนที่ทั่วไปและคุณสมบัติใช้แผนที่

วิธีนี้จะได้ผลแม้ว่าจะมีคำเตือน

    Properties properties = new Properties();
    Map<String, String> map = new HashMap(properties);


0

สิ่งแรก,

คลาสคุณสมบัติขึ้นอยู่กับ Hashtable ไม่ใช่ Hashmap คลาสคุณสมบัติโดยทั่วไปจะขยาย Hashtable

ไม่มีตัวสร้างดังกล่าวในคลาส HashMap ซึ่งรับวัตถุคุณสมบัติและส่งคืนวัตถุแฮชแมปให้คุณ ดังนั้นสิ่งที่คุณทำจึงไม่ถูกต้อง คุณควรจะสามารถส่งวัตถุคุณสมบัติเป็นข้อมูลอ้างอิงที่แฮชได้


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