Cara yang efisien untuk mengulang dan menyalin nilai-nilai HashMap

9

Saya ingin mengonversi:

Map<String, Map<String, List<Map<String, String>>>> inputMap 

untuk:

Map<String, Map<String, CustomObject>> customMap

inputMapdisediakan dalam konfigurasi dan siap tetapi saya perlu customMapmemformat. CustomObject akan diturunkan dari List<Map<String, String>>penggunaan beberapa baris kode dalam suatu fungsi.

Saya telah mencoba cara normal untuk mengulangi input peta dan menyalin nilai-nilai kunci di customMap. Apakah ada cara efisien untuk melakukan itu menggunakan Java 8 atau cara pintas lainnya?

Map<String, Map<String, List<Map<String, String>>>> configuredMap = new HashMap<>();
Map<String, Map<String, CustomObj>> finalMap = new HashMap<>();


for (Map.Entry<String, Map<String, List<Map<String, String>>>> attributeEntry : configuredMap.entrySet()) {
    Map<String, CustomObj> innerMap = new HashMap<>();
    for (Map.Entry<String, List<Map<String, String>>> valueEntry : attributeEntry.getValue().entrySet()) {
        innerMap.put(valueEntry.getKey(), getCustomeObj(valueEntry.getValue()));
    }
    finalMap.put(attributeEntry.getKey(), innerMap);
}

private CustomObj getCustomeObj(List<Map<String, String>> list) {
    return new CustomObj();
}
hantu pengendara
sumber
Harap format kode dengan benar.
akuzminykh
1
Pernahkah Anda berpikir untuk membuat fasad, daripada menyalin?
ControlAltDel
Tidak mungkin ada cara yang lebih efisien. Semua operasi itu harus dilakukan. Tetapi kode ini tidak benar-benar berfungsi. Anda tidak memasukkan daftar ke objek kustom.
user207421

Jawaban:

2

Salah satu solusi adalah untuk streaming entrySetdari inputMap, dan kemudian menggunakan Collectors#toMapdua kali (sekali untuk bagian luar Map, dan sekali untuk inner Map):

Map<String, Map<String, CustomObj>> customMap = inputMap.entrySet()
        .stream()
        .collect(Collectors.toMap(Function.identity(), entry -> {
            return entry.getValue()
                        .entrySet()
                        .stream()
                        .collect(Collectors.toMap(Function.identity(), 
                            entry -> getCustomeObj(entry.getValue())));
        }));
Jacob G.
sumber
Anda dapat menghilangkan {}dan mengembalikan pernyataan dalam lambda, sesuatu seperti ini:.collect(Collectors.toMap(Function.identity(), entry -> entry.getValue() .entrySet() .stream() .collect(Collectors.toMap(Function.identity(), entry -> getCustomeObj(entry.getValue()))); ));
SHoko
3
@Shoko Benar, tapi saya pikir itu akan terlihat kurang mudah dibaca tanpa blok
Jacob G.
1

Anda dapat melakukan streaming, tetapi itu tidak akan terlihat mudah dibaca; setidaknya bagi saya. Jadi, jika Anda memiliki metode:

static CustomObject fun(List<Map<String, String>> in) {
    return .... // whatever processing you have here
}

Anda masih bisa menggunakan java-8sintaks, tetapi dalam bentuk yang berbeda:

    Map<String, Map<String, CustomObject>> customMap = new HashMap<>();

    inputMap.forEach((key, value) -> {

        value.forEach((innerKey, listOfMaps) -> {

            Map<String, CustomObject> innerMap = new HashMap<>();
            innerMap.put(innerKey, fun(listOfMaps));
            customMap.put(key, innerMap);

        });
    });

Jika Anda bisa membuat peta bagian dalam immutable, Anda bisa membuatnya lebih pendek:

inputMap.forEach((key, value) -> {
      value.forEach((innerKey, listOfMaps) -> {
          customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
      });
});
Eugene
sumber
1

Streaming IMHO bukan ide yang buruk. Tidak ada alat yang buruk. Itu tergantung pada bagaimana Anda menggunakannya.


Dalam kasus khusus ini saya akan mengekstrak pola berulang menjadi metode utilitas:

public static <K, V1, V2> Map<K, V2> transformValues(Map<K, V1> map, Function<V1, V2> transformer) {
    return map.entrySet()
              .stream()
              .collect(toMap(Entry::getKey, e -> transformer.apply(e.getValue())));
}

Metode di atas dapat diimplementasikan dengan menggunakan pendekatan apa pun, meskipun saya pikir Stream APIsangat cocok di sini.


Setelah Anda mendefinisikan metode utilitas, itu dapat digunakan sesederhana berikut:

Map<String, Map<String, CustomObj>> customMap = 
    transformValues(inputMap, attr -> transformValues(attr, this::getCustomObj));

Transformasi aktual secara efektif adalah satu liner. Jadi dengan tepat JavaDocuntuk transformValuesmetode kode hasil cukup mudah dibaca dan dipelihara.

ETO
sumber
1

Bagaimana Collectors.toMapdengan entri baik di level luar dan dalam seperti:

Map<String, Map<String, CustomObj>> finalMap = configuredMap.entrySet()
        .stream()
        .collect(Collectors.toMap(Map.Entry::getKey,
                attributeEntry -> attributeEntry.getValue().entrySet()
                        .stream()
                        .collect(Collectors.toMap(Map.Entry::getKey,
                                valueEntry -> getCustomeObj(valueEntry.getValue())))));
Naman
sumber