Kaedah Kilang Kemudahan Java untuk Koleksi

1. Gambaran keseluruhan

Java 9 membawa gula sintaksis yang telah lama ditunggu-tunggu untuk membuat contoh Koleksi kecil yang tidak dapat diubah menggunakan kod satu pelapik ringkas. Pada JEP 269, kaedah kilang kemudahan baru akan dimasukkan dalam JDK 9.

Dalam artikel ini, kami akan membahas penggunaannya bersama dengan perincian pelaksanaannya.

2. Sejarah dan Motivasi

Membuat Koleksi kecil yang tidak berubah di Jawa sangat verbose menggunakan kaedah tradisional.

Mari kita ambil contoh Set :

Set set = new HashSet(); set.add("foo"); set.add("bar"); set.add("baz"); set = Collections.unmodifiableSet(set);

Itu terlalu banyak kod untuk tugas sederhana dan mungkin boleh dilakukan dalam satu ungkapan.

Perkara di atas juga berlaku untuk Peta.

Namun, untuk List , ada kaedah kilang:

List list = Arrays.asList("foo", "bar", "baz");

Walaupun penciptaan Senarai ini lebih baik daripada inisialisasi konstruktor, ini kurang jelas kerana intuisi umum adalah tidak melihat kelas Arrays untuk kaedah membuat Senarai :

Terdapat kaedah lain untuk mengurangkan verbositi seperti teknik inisial pendakap dua :

Set set = Collections.unmodifiableSet(new HashSet() {{ add("foo"); add("bar"); add("baz"); }});

atau dengan menggunakan Java 8 Streams :

Stream.of("foo", "bar", "baz") .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

Teknik pendakap ganda hanya sedikit lebih sedikit tetapi sangat mengurangkan kebolehbacaan (dan dianggap sebagai anti-corak).

Versi Java 8, bagaimanapun, adalah ungkapan satu baris, dan ia juga mempunyai beberapa masalah. Pertama, ia tidak jelas dan intuitif. Kedua, ia masih verbose. Ketiga, ia melibatkan penciptaan objek yang tidak perlu. Dan keempat, kaedah ini tidak dapat digunakan untuk membuat Peta .

Untuk merumuskan kekurangan, tidak ada pendekatan di atas yang menangani kes penggunaan khusus yang mewujudkan masalah kelas kelas Koleksi kecil yang tidak dapat diubah .

3. Penerangan dan Penggunaan

Kaedah statis telah disediakan untuk antara muka List , Set , dan Map yang mengambil elemen sebagai argumen dan mengembalikan instance List , Set , dan Map , masing-masing.

Kaedah ini dinamakan (…) untuk ketiga-tiga antara muka.

3.1. Senaraikan dan Tetapkan

Tanda tangan dan ciri kaedah kilang List and Set adalah sama:

static  List of(E e1, E e2, E e3) static  Set of(E e1, E e2, E e3)

penggunaan kaedah:

List list = List.of("foo", "bar", "baz"); Set set = Set.of("foo", "bar", "baz");

Seperti yang kita lihat, ia sangat ringkas, pendek, dan ringkas.

Sebagai contoh, kami telah menggunakan kaedah dengan mengambil tiga elemen tepat sebagai parameter dan mengembalikan Senarai / Set ukuran 3.

Tetapi, terdapat 12 versi kaedah ini - sebelas dengan parameter 0 hingga 10 dan satu dengan var-args:

static  List of() static  List of(E e1) static  List of(E e1, E e2) // ....and so on static  List of(E... elems)

Untuk kebanyakan tujuan praktikal, 10 elemen akan mencukupi, tetapi jika lebih banyak diperlukan, versi var-args dapat digunakan.

Sekarang, kita mungkin bertanya, apa gunanya memiliki 11 kaedah tambahan jika ada versi var-args yang dapat berfungsi untuk sejumlah elemen.

Jawapannya ialah prestasi. Setiap panggilan kaedah var-args secara implisit membuat array. Mempunyai kaedah yang terlalu banyak mengelakkan penciptaan objek yang tidak perlu dan pengumpulan sampah di atasnya. Sebaliknya, Arrays.asList selalu membuat susunan tersirat dan, oleh itu, kurang efisien apabila bilangan elemennya rendah.

Semasa pembuatan Set menggunakan kaedah kilang, jika elemen pendua dilewatkan sebagai parameter, maka IllegalArgumentException dilemparkan pada waktu runtime:

@Test(expected = IllegalArgumentException.class) public void onDuplicateElem_IfIllegalArgExp_thenSuccess() { Set.of("foo", "bar", "baz", "foo"); }

Perkara penting yang perlu diperhatikan di sini ialah kerana kaedah kilang menggunakan generik, jenis primitif mendapat autoboks.

Jika pelbagai jenis primitif diluluskan, yang List of lokasi itu jenis primitif dikembalikan.

Sebagai contoh:

int[] arr = { 1, 2, 3, 4, 5 }; List list = List.of(arr);

Dalam kes ini, Senarai ukuran 1 dikembalikan dan elemen pada indeks 0 mengandungi array.

3.2. Peta

Tandatangan kaedah kilang Peta adalah:

static  Map of(K k1, V v1, K k2, V v2, K k3, V v3)

dan penggunaan:

Map map = Map.of("foo", "a", "bar", "b", "baz", "c");

Begitu juga dengan List and Set , kaedah (…) berlebihan untuk mempunyai 0 hingga 10 pasangan nilai-kunci.

Bagi Peta , terdapat kaedah yang berbeza untuk lebih dari 10 pasangan nilai-kunci:

static  Map ofEntries(Map.Entry... entries)

dan penggunaannya:

Map map = Map.ofEntries( new AbstractMap.SimpleEntry("foo", "a"), new AbstractMap.SimpleEntry("bar", "b"), new AbstractMap.SimpleEntry("baz", "c"));

Memberi nilai pendua untuk Key akan membuang IllegalArgumentException :

@Test(expected = IllegalArgumentException.class) public void givenDuplicateKeys_ifIllegalArgExp_thenSuccess() { Map.of("foo", "a", "foo", "b"); }

Again, in the case of Map too, the primitive types are autoboxed.

4. Implementation Notes

The collections created using the factory methods are not commonly used implementations.

For example, the List is not an ArrayList and the Map is not a HashMap. Those are different implementations that are introduced in Java 9. These implementations are internal and their constructors have restricted access.

In this section, we'll see some important implementation differences which are common to all the three types of collections.

4.1. Immutable

The collections created using factory methods are immutable, and changing an element, adding new elements, or removing an element throws UnsupportedOperationException:

@Test(expected = UnsupportedOperationException.class) public void onElemAdd_ifUnSupportedOpExpnThrown_thenSuccess() { Set set = Set.of("foo", "bar"); set.add("baz"); }
@Test(expected = UnsupportedOperationException.class) public void onElemModify_ifUnSupportedOpExpnThrown_thenSuccess() { List list = List.of("foo", "bar"); list.set(0, "baz"); } 

On the other hand, the collection returned from Arrays.asListis mutable. Therefore, it's possible to change or remove the existing elements. Similar to List.of, we can't add new elements to a list returned from Arrays.asList.

@Test(expected = UnsupportedOperationException.class) public void onElemRemove_ifUnSupportedOpExpnThrown_thenSuccess() { Map map = Map.of("foo", "a", "bar", "b"); map.remove("foo"); }

4.2. No null Element Allowed

In the case of List and Set, no elements can be null. In the case of a Map, neither keys nor values can be null. Passing null argument throws a NullPointerException:

@Test(expected = NullPointerException.class) public void onNullElem_ifNullPtrExpnThrown_thenSuccess() { List.of("foo", "bar", null); }

As opposed to List.of, the Arrays.asList method accepts null values.

4.3. Value-Based Instances

The instances created by factory methods are value-based. This means that factories are free to create a new instance or return an existing instance.

Hence, if we create Lists with same values, they may or may not refer to the same object on the heap:

List list1 = List.of("foo", "bar"); List list2 = List.of("foo", "bar");

Dalam kes ini, list1 == list2 mungkin atau tidak dapat dinilai menjadi benar bergantung pada JVM.

4.4. Serialisasi

Koleksi yang dibuat dari kaedah kilang boleh Serializable jika unsur koleksi dapat Serializable.

5. Kesimpulan

Dalam artikel ini, kami memperkenalkan kaedah kilang baru untuk Koleksi yang diperkenalkan di Java 9.

Kami membuat kesimpulan mengapa ciri ini adalah perubahan yang dialu-alukan dengan menggunakan beberapa kaedah masa lalu untuk membuat koleksi yang tidak dapat diubah. Kami merangkumi penggunaannya dan menyoroti perkara utama yang perlu dipertimbangkan semasa menggunakannya.

Akhirnya, kami menjelaskan bahawa koleksi ini berbeza dengan pelaksanaan yang biasa digunakan dan menunjukkan perbezaan utama.

Kod sumber lengkap dan ujian unit untuk artikel ini boleh didapati di GitHub.