Mengonversi java.util.Properties menjadi HashMap <String, String>

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

java.util.Propertiesadalah implementasi dari java.util.Map, dan java.util.HashMapkonstruktor menerima Mapparameter tipe. Jadi, mengapa harus diubah secara eksplisit?

Tardis Xu
sumber

Jawaban:

86

Ini karena PropertiesmeluasHashtable<Object, Object> (yang, pada gilirannya, mengimplementasikan Map<Object, Object>). Anda mencoba memasukkannya ke file Map<String, String>. Oleh karena itu tidak kompatibel.

Anda perlu memasukkan properti string satu per satu ke dalam peta Anda ...

Contohnya:

for (final String name: properties.stringPropertyNames())
    map.put(name, properties.getProperty(name));
fge
sumber
1
Ya, tapi bukan itu masalahnya di sini: argumen umum tidak cocok. Anda dapat memberi makan apa pun yang Anda inginkan Hashtable<Object, Object>, bahkan hal-hal yang bukan string - bahkan kunci yang bukan string.
fge
@assylias: Tidak, itu juga tidak dapat dikompilasi.
Jon Skeet
13
di 1,8 Anda bisa melakukan properties.forEach ((k, v) -> map.put ((String) k, (String) v));
ModdyFire
1
Atau jika Anda belum memiliki peta di properti tangan.entrySet (). Stream (). Collect (Collectors.toMap (e -> (String) e.getKey (), e -> (String) e.getValue ( )))
Tonsic
46

Cara efisien untuk melakukannya hanya dengan mentransmisikan ke Peta umum sebagai berikut:

Properties props = new Properties();

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

Ini akan mengubah a Map<Object, Object>menjadi Peta mentah, yang "ok" untuk kompilator (hanya peringatan). Setelah kita memiliki mentah Mapitu akan dilemparkan Map<String, String>yang juga akan menjadi "ok" (peringatan lain). Anda dapat mengabaikannya dengan anotasi@SuppressWarnings({ "unchecked", "rawtypes" })

Ini akan berhasil karena di JVM objek tidak benar-benar memiliki tipe generik. Jenis generik hanyalah trik yang memverifikasi berbagai hal pada waktu kompilasi.

Jika beberapa kunci atau nilai bukan String, ini akan menghasilkan ClassCastExceptionkesalahan. Dengan arus Propertiesimplementasi ini sangat tidak mungkin terjadi, selama Anda tidak menggunakan metode panggilan bisa berubah dari super Hashtable<Object,Object>dari Properties.

Jadi, jika tidak melakukan hal-hal buruk dengan contoh Properti Anda, ini adalah cara yang tepat.

padilo
sumber
Pertanyaannya adalah untuk mengubah ke HashMap. Tidak ada Peta.
AlikElzin-kilaka
3
Ya, judul pertanyaan mengatakan itu, tetapi tujuannya adalah untuk memiliki Mapcontoh setidaknya pada kode yang diberikan, jadi saya pikir inilah yang dia butuhkan
padilo
Meskipun saya menyukai solusi purist lainnya, solusi ini berguna bagi saya karena hanya satu baris sederhana.
Alfonso Nishikawa
27

Bagaimana dengan ini?

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

Akan menyebabkan peringatan, tetapi bekerja tanpa iterasi.

Seshadri Sastry
sumber
4
@ fge: Ini bukan a Map<Object, Object>, ini adalah Mapargumen (tipe mentah). Jawaban ini benar
Lukas Eder
2
Huh, ya saya coba dengan Eclipse. Salah satu perbedaan umum antara Eclipse dan javac lagi? .... tidak, bekerja dengan javac juga
Lukas Eder
4
Ini berfungsi tetapi iterasi masih terjadi. Jika Anda melihat kode sumber untuk HashMap, konstruktor pada dasarnya melakukan iterasi melalui parameter peta generik. Jadi waktu komputasi tidak berubah tetapi kodenya tentu lebih ringkas.
Simeon G
Seperti jawaban sebelumnya yang dinyatakan, tidak perlu membuat instance baru dan melakukan iterasi pada objek properti. Cukup gunakan urutan pemeran: (Map<String, String>) ((Map) properties)
Ricardo Veloso
22

Cara Java 8:

properties.entrySet().stream().collect(
    Collectors.toMap(
         e -> e.getKey().toString(),
         e -> e.getValue().toString()
    )
);
Ben McCann
sumber
Apakah ada cara untuk menggunakan referensi metode daripada lambda. Karena masalah sonar qube.
Viyaan Jhiingade
16

Propertiesmengimplementasikan Map<Object, Object>- tidak Map<String, String>.

Anda mencoba memanggil konstruktor ini:

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

... dengan Kdan Vkeduanya sebagai String.

Tapi Map<Object, Object>bukan Map<? extends String, ? extends String>... itu bisa berisi kunci dan nilai non-string.

Ini akan berhasil:

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

... tapi itu tidak akan berguna bagi Anda.

Pada dasarnya, Propertiesseharusnya tidak pernah dijadikan subclass dari HashTable... itulah masalahnya. Sejak v1, itu selalu dapat menyimpan kunci dan nilai non-String, meskipun itu bertentangan dengan maksudnya. Jika komposisi telah digunakan sebagai gantinya, API hanya bisa bekerja dengan kunci / nilai string, dan semuanya akan berjalan dengan baik.

Anda mungkin menginginkan sesuatu seperti ini:

Map<String, String> map = new HashMap<String, String>();
for (String key : properties.stringPropertyNames()) {
    map.put(key, properties.getProperty(key));
}
Jon Skeet
sumber
ternyata juga tidak mungkin untuk dilakukan secara eksplisit Properties<String,String> properties = new Properties<String,String>();. Aneh.
eis
1
@eis Itu dengan desain, Propertiesitu sendiri tidak generik.
Mattias Buelens
Saya lebih suka mengatakan itu dengan serangkaian pilihan yang tidak menguntungkan daripada dengan desain, tapi ya.
eis
2
@eis: Tidak, itu dengan desain bahwa Properties dimaksudkan untuk menjadi peta string-to-string. Masuk akal jika ini tidak umum. Ini tidak masuk akal bahwa Anda dapat menambahkan kunci non-string / nilai.
Jon Skeet
8

Jika Anda tahu bahwa Propertiesobjek Anda hanya berisi <String, String>entri, Anda dapat menggunakan tipe mentah:

Properties properties = new Properties();
Map<String, String> map = new HashMap<String, String>((Map) properties);
Lukas Eder
sumber
4

Masalahnya adalah yang Propertiesmengimplementasikan Map<Object, Object>, sedangkan HashMapkonstruktor mengharapkan a Map<? extends String, ? extends String>.

Jawaban ini menjelaskan keputusan (yang cukup berlawanan dengan intuisi) ini. Singkatnya: sebelum Java 5, Propertiesdiimplementasikan Map(karena tidak ada obat generik saat itu). Ini berarti Anda bisa meletakkan apa saja Object di dalam sebuah Propertiesbenda. Ini masih dalam dokumentasi:

Karena Propertiesmewarisi dari Hashtable, metode putdan putAlldapat diterapkan ke Propertiesobjek. Penggunaannya sangat tidak disarankan karena memungkinkan pemanggil untuk memasukkan entri yang kunci atau nilainya bukan Strings. Sebagai setPropertygantinya, metode ini harus digunakan.

Untuk menjaga kompatibilitas dengan ini, para desainer tidak punya pilihan lain selain membuatnya mewarisi Map<Object, Object>di Java 5. Ini adalah hasil yang disayangkan dari upaya untuk kompatibilitas mundur penuh yang membuat kode baru tidak perlu berbelit-belit.

Jika Anda hanya pernah menggunakan properti string di Propertiesobjek Anda, Anda harus bisa lolos dengan cast yang tidak dicentang di konstruktor Anda:

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

atau tanpa salinan apa pun:

Map<String, String> map = (Map<String, String>) properties;
Mattias Buelens
sumber
Ini adalah tanda tangan konstruktor HashMap public HashMap(Map<? extends K, ? extends V> m). Itu tidak mengharapkanMap<String, String>
Mubin
@Mubin Oke, saya terlalu menyederhanakan masalah. Namun, argumennya tetap: a Map<Object, Object>tidak dapat digunakan untuk argumen formal tipe` Map <? memperluas String,? memperluas String> `.
Mattias Buelens
2

ini hanya karena konstruktor HashMap memerlukan argumen tipe generik Map dan Properties mengimplementasikan Map.

Ini akan berhasil, meskipun dengan peringatan

    Properties properties = new Properties();
    Map<String, String> map = new HashMap(properties);
Evgeniy Dorofeev
sumber
1

Anda dapat menggunakan ini:

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

props.forEach((key, value) -> map.put(key.toString(), value.toString()));
Raman Sahasi
sumber
0

Hal pertama,

Kelas properti didasarkan pada Hashtable dan bukan Hashmap. Kelas properti pada dasarnya memperluas Hashtable

Tidak ada konstruktor dalam kelas HashMap yang mengambil objek properti dan mengembalikan Anda objek hashmap. Jadi apa yang Anda lakukan TIDAK benar. Anda harus dapat mentransmisikan objek properti ke referensi yang memiliki hashtable.

Juned Ahsan
sumber
0

saya menggunakan ini:

for (Map.Entry<Object, Object> entry:properties.entrySet()) {
    map.put((String) entry.getKey(), (String) entry.getValue());
}
atom
sumber