Saya baru mengenal Java dan baru-baru ini saya mengetahui bahwa terkadang penting untuk menyalin Koleksi dan membuat tampilan yang tidak dapat dimodifikasi sehingga data di dalamnya tetap aman dan tidak berubah. Ketika saya mencoba mempraktekkan ini (unmodifiableMap2), saya mendapat peringatan dari IDEA bahwa

unmodifiableMap Dapat diganti dengan panggilan 'Map.copyOf'

Itu ditransfer untuk saya karena saya pikir unmodifiableMap bukan hanya salinan dari peta yang mendasarinya. Selain itu, ketika saya mencoba membuat unmodifiableMap yang sama dengan cara lain (unmodifiableMap1), peringatan tidak muncul!

Bagaimana saya harus memahami perilaku IDEA ini?

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class test {
    public static void main(String[] args) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);

        Map<Integer, Integer> map1 = new HashMap<>(map);

        Map<Integer, Integer> unmodifiableMap1 = Collections.unmodifiableMap(map1);

        Map<Integer, Integer> unmodifiableMap2 = Collections.unmodifiableMap(new HashMap<>(map););

    }
}

enter image description here

5
Saibō 11 Januari 2021, 00:22

3 jawaban

Jawaban Terbaik

Peta yang tidak dapat dimodifikasi menggunakan referensi yang sudah ada ke peta baik-baik saja, dan ada banyak alasan Anda mungkin ingin melakukan ini.

Pertimbangkan kelas ini:

class Foo {
    private final Map<String, String> fooMap = new HashMap<>();

    // some methods which mutate the map

    public Map<String, String> getMap() {
        return Collections.unmodifiableMap(fooMap);
    }
}

Apa yang dilakukan kelas ini adalah memberikan tampilan hanya-baca dari peta yang diringkasnya. Kelas dapat memastikan bahwa klien yang menggunakan peta tidak dapat mengubahnya, mereka hanya dapat melihat isinya. Mereka juga akan dapat melihat pembaruan apa pun pada entri jika mereka menyimpan referensi untuk beberapa waktu.

Jika kami mencoba mengekspos tampilan hanya-baca dengan menyalin peta, akan membutuhkan waktu dan memori untuk melakukan penyalinan dan klien tidak akan melihat perubahan apa pun karena kedua peta merupakan contoh yang berbeda - sumber dan salinannya.

Namun dalam hal ini:

Collections.unmodifiableMap(new HashMap<>(map));

Anda pertama-tama menyalin peta ke peta hash baru dan kemudian meneruskan salinan itu ke Collections.unmodifiableMap. Hasilnya secara efektif konstan. Anda tidak memiliki referensi ke salinan yang Anda buat dengan new HashMap<>(map), dan Anda juga tidak bisa mendapatkannya*.

Jika yang Anda inginkan adalah peta konstan, maka Map.copyOf adalah cara yang lebih ringkas untuk mencapainya, jadi IntelliJ menyarankan Anda untuk menggunakannya.

Dalam kasus pertama, karena referensi ke peta sudah ada, IntelliJ tidak dapat membuat kesimpulan yang sama tentang maksud Anda sehingga tidak memberikan saran seperti itu.

Anda dapat melihat tiket IntelliJ untuk fitur ini jika Anda suka, meskipun itu tidak menjelaskan mengapa keduanya pada dasarnya setara, hanya saja mereka.


* yah, Anda mungkin bisa melalui refleksi, tetapi IntelliJ berasumsi bahwa Anda tidak akan melakukannya

3
Michael 10 Januari 2021, 21:40

Map.copyOf() membuat salinan dari instance Map yang diberikan, tetapi hal itu mengharuskan tidak ada nilai dalam peta yang null. Biasanya, ini masalahnya, tetapi ini bukan persyaratan ketat untuk Map secara umum.

java.util.Collections.unmodifiableMap() hanya membungkus referensi ke instance Map yang diberikan. Ini berarti bahwa penerima tidak dapat mengubah peta, tetapi modifikasi pada peta asli (yang merupakan argumen untuk unmodifiableMap()) dapat dilihat oleh penerima.

Dengan asumsi kami memiliki dua utas, satu mengulangi peta yang tidak dapat dimodifikasi, sementara yang lain memodifikasi yang asli. Akibatnya, Anda mungkin mendapatkan ConcurrentModificationException untuk operasi pada peta yang tidak dapat dimodifikasi … tidak lucu untuk men-debug hal itu!

Ini tidak dapat terjadi dengan salinan yang dibuat oleh Map.copyOf(). Tetapi ini ada harganya: dengan salinan, Anda membutuhkan dua kali lipat jumlah memori untuk peta (kira-kira). Untuk peta yang sangat besar, ini dapat menyebabkan kekurangan memori hingga OutOfMemoryError. Juga tidak menyenangkan untuk di-debug!

Selain itu, hanya membungkus peta yang ada mungkin jauh lebih cepat daripada menyalinnya.

Jadi tidak ada solusi terbaik secara umum, tetapi untuk sebagian besar skenario, saya memiliki preferensi untuk menggunakan Map.copyOf() ketika saya membutuhkan peta yang tidak dapat dimodifikasi.


Sampel dalam pertanyaan tidak membungkus instance asli Map, tetapi membuat salinan sebelum membungkusnya (baik dalam garisnya sendiri, atau dengan cepat). Ini menghilangkan potensi masalah dengan modifikasi 'di bawah tenda', tetapi dapat membawa masalah memori.

Dari pengalaman saya sejauh ini, Map.copyOf( map ) terlihat lebih efisien daripada Collections.unmodifiableMap( new HashMap( map ) ).


Omong-omong: Map.copyOf() mengembalikan peta yang menyerupai HashMap; ketika Anda menyalin TreeMap dengannya, urutan pengurutan akan hilang, sementara pembungkusan dengan unmodifiableMap() mempertahankan implementasi Map yang mendasarinya dan oleh karena itu juga urutan pengurutannya. Jadi ketika ini penting, Anda dapat menggunakan Collections.unmodifiableMap( new TreeMap( map ) ), sementara Map.copyOf() tidak berfungsi di sini.

2
tquadrat 11 Januari 2021, 11:20

Map.copyOf(map) sepenuhnya setara dengan Collections.unmodifiableMap(new HashMap<>(map)).

Juga tidak ada penyalinan yang dalam. Tapi itu benar-benar lebih pendek untuk dilakukan Maps.copyOf(map).

0
Louis Wasserman 11 Januari 2021, 00:21