Struktur data di Jawa. Metode kelas pembantu yang berguna

Hai habr!

Saya seorang Insinyur Perangkat Lunak di EPAM. Selama lebih dari 8 tahun saya telah bekerja dengan kode warisan yang ditulis di Jawa (mengantisipasi komentar, saya perhatikan bahwa pemahaman dan toleransi untuk warisan dimulai jauh sebelum EPAM, sebagai kesimpulan Anda akan menemukan jawabannya mengapa). Seringkali dalam pekerjaan saya menemukan kesalahan berulang yang sama. Ini mendorong saya untuk menulis catatan, dan saya ingin memulai dengan struktur data dan kelas pembantu Koleksi dan Array . Untuk beberapa alasan, beberapa pengembang mengabaikan penggunaannya, dan sia-sia

Pengembang Java sering harus berurusan dengan berbagai struktur data. Ini bisa berupa array, semua jenis koleksi atau implementasi Peta . Tampaknya segala sesuatu dengan mereka jelas dan dapat dimengerti, tetapi ada beberapa hal kecil yang mudah tersandung.

Catatan ini mungkin berguna untuk pemula yang belum tahu nuansa ini, dan pengembang berpengalaman yang mungkin melupakan sebagian dari ini.

gambar
Foto oleh ammiel jr di Unsplash

CAT


Saya ingin segera memesan bahwa materi ini relevan untuk Java 8. Jelas bahwa beberapa hal telah dilakukan dengan lebih baik di Java 9+, tetapi sebagian besar proyek besar paling sering menggunakan versi Java 8 (dan kadang-kadang Java 6).

Apa cara terbaik untuk mendapatkan koleksi berbasis array?


Saya sarankan mulai dengan pembentukan koleksi berdasarkan array.

Paling sering, metode ini terjadi:

Integer[] someArray = {9, 10, 11, 12}; List<Integer> list = Arrays.asList(someArray); 

Ini memang berhasil, tetapi apakah semuanya baik-baik saja dengan itu? Dan adakah solusi alternatif?

Dua minus dari pendekatan ini muncul sekaligus:

  • Pertama, metode Arrays.asList mengembalikan Daftar . Tetapi bagaimana jika kita membutuhkan implementasi Collection yang lain? Arrays.asList tidak akan mengizinkan ini, tetapi suatu alternatif akan dipertimbangkan sedikit lebih jauh.
  • Kedua, Daftar diperoleh dengan memanggil Arrays.asList tidak mendukung pengubahan ukuran. Saya pikir banyak yang datang dengan pengecualian yang timbul dari bekerja dengan daftar seperti itu.

Di antarmuka Koleksi , Anda dapat menemukan alternatif metode Arrays.asList - metode Collections.addAll . Berikut cara menggunakannya:

 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray); 

Atau sederhananya:

 Collections.addAll(collection, 11, 12, 13, 14); 

Metode Collections.addAll menerima objek Koleksi dan array di input. Alih-alih array, Anda juga bisa menentukan elemen yang dipisahkan oleh koma.

Apa manfaat dari Collections.addAll over Arrays.asList ?

  • Untuk mulai dengan, ketika membuat koleksi berdasarkan array Collections.addAll , ia bekerja jauh lebih cepat daripada metode addAll koleksi dengan input dari Array.asList . Ini dapat ditemukan di JavaDoc dari metode ini:
    Perilaku metode kenyamanan ini identik dengan perilaku c.addAll (Arrays.asList (elemen)), tetapi metode ini cenderung berjalan secara signifikan lebih cepat di sebagian besar
  • Selain itu, Collections.addAll bekerja tidak hanya dengan Daftar , tetapi dengan koleksi lainnya.
  • Dan saat menggunakan metode ini, tidak ada masalah mengubah ukuran.

Apa cara termudah untuk mencetak array, array multidimensi, atau koleksi?


Mari kita beralih ke masalah mendapatkan representasi cetak dari array dan koleksi.

Jika kita hanya membuat System.out.println (someArray) , kita mendapatkan sesuatu seperti ini:
[Ljava.lang.Integer; @ 6d06d69c.
Hasil serupa diharapkan ketika menggunakan metode toString () pada array.
Untuk menampilkan array, metode Arrays.toString (...) datang untuk menyelamatkan .

 Integer[] someArray = new Integer[]{1, 2, 3}; System.out.println(Arrays.toString(someArray)); 

Output untuk baris ini adalah:

 [1, 2, 3] 

Jika kita berbicara tentang array multidimensi, maka Anda dapat menggunakan metode: Arrays.deeptoString .

 int[][] a = { {1, 2, 3}, {4, 5, 6} }; System.out.println(Arrays.deepToString(a)); 

Output dari cuplikan ini adalah:

  [[1, 2, 3], [4, 5, 6]] 


Jadi, tidak perlu menyortir array melalui loop apa pun secara manual untuk menampilkan elemen-elemennya, cukup menggunakan metode ini.

Sedangkan untuk koleksi atau implementasi Peta , tidak ada masalah. Semua struktur data kecuali array biasanya adalah output.

Misalkan ada contoh:

 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap); 

Perhatikan dalam output di bawah ini bahwa set dan Peta ditampilkan dalam format yang mudah dibaca:

 [1, 2] 
 {1 = Beberapa 1, 2 = Beberapa 2} 


Seberapa mudah membandingkan array satu sama lain?


Ada beberapa situasi ketika Anda perlu membandingkan array. Ada metode di kelas Array yang memungkinkan perbandingan seperti itu. Metode Arrays.equals membandingkan jumlah elemen dan memeriksa kesetaraan elemen yang sesuai.

Katakanlah kita memiliki kelas Elemen dengan satu bidang dan sederajat tertentu

 private class Element { final String name; private Element(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); } } 

Tentukan tiga array:

 Element[] firstElementArray = { new Element("a"), new Element("b"), new Element("c") }; Element[] secondElementArray = {new Element("c"), new Element("b"), new Element("a") }; Element[] thirdElementArray = { new Element("a"), new Element("b"), new Element("c") }; 

Perhatikan bahwa array pertama dan ketiga memiliki elemen dalam urutan yang sama.
Sekarang Anda dapat melakukan perbandingan menggunakan metode Arrays.equals .

 System.out.println("first equals to second? " + Arrays.equals(firstElementArray, secondElementArray)); System.out.println("second equals to third? " + Arrays.equals(secondElementArray, thirdElementArray)); System.out.println("first equals to third? " + Arrays.equals(firstElementArray, thirdElementArray)); 

Hasilnya adalah sebagai berikut:

 pertama sama dengan kedua?  salah 
 kedua sama dengan ketiga?  salah 
 pertama sama dengan ketiga?  benar 


Bagaimana cara menyalin array secara efisien?


Seringkali Anda dapat melihat dalam kode menyalin array secara manual menggunakan loop. Namun, ada metode System.arraycopy yang akan menyalin lebih cepat.

Saya sarankan lihat contoh sederhana seperti ini:

 Element[] elements = { new Element("a"), new Element("b"), new Element("c") }; Element[] copyOfElements = new Element[elements.length]; System.arraycopy(elements, 0, copyOfElements, 0, elements.length); System.out.println(Arrays.toString(copyOfElements)); 

Kami memiliki berbagai elemen. Kami membuat array kosong dengan panjang yang sama dan menyalin semua elemen dari yang pertama ke yang kedua. Hasilnya, kami memperoleh kesimpulan berikut:

 [Elemen {name = 'a'}, Elemen {name = 'b'}, Elemen {name = 'c'}] 


Bagaimana cara mengurutkan array atau koleksi dengan cara yang berbeda?


Array dapat diurutkan menggunakan metode Arrays.sort (someArray) . Jika Anda ingin mengurutkan array dalam urutan terbalik, Anda dapat meneruskan Collections.reverseOrder () sebagai parameter kedua ke input ke metode ini.

Misalnya, ada array yang kami sortir langsung dan kemudian dalam urutan terbalik:

 String[] someArray = new String[]{"b", "a", "c"}; Arrays.sort(someArray); System.out.println(Arrays.toString(someArray)); Arrays.sort(someArray, Collections.reverseOrder()); System.out.println(Arrays.toString(someArray)); 

Kesimpulannya adalah sebagai berikut:

 [a, b, c] 
 [c, b, a] 


Selain pengurutan langsung dan mundur, kadang-kadang terjadi bahwa Anda perlu mengurutkan array string terlepas dari kasus. Ini mudah dilakukan dengan meneruskan String.CASE_INSENSITIVE_ORDER sebagai parameter kedua ke Arrays.sort .

Sayangnya, Collections.sort hanya memungkinkan implementasi Daftar untuk diurutkan.

Algoritma apa yang memilah Java?


Hal terakhir yang disebutkan ketika berbicara tentang pengurutan di Jawa adalah bahwa di Jawa, "pengurutan sederhana" digunakan untuk jenis yang paling sederhana, dan "stabil gabungan" digunakan untuk objek. Jadi Anda tidak boleh menghabiskan sumber daya untuk mengembangkan implementasi Anda sendiri dari metode penyortiran sampai profiler menunjukkan bahwa itu perlu.

Bagaimana jika kita memiliki array dan metode menerima Iterable?


Saya mengusulkan sekarang untuk beralih ke pertanyaan seperti melewati array ke metode yang membutuhkan Iterable . Biarkan saya mengingatkan Anda bahwa Iterable adalah antarmuka yang berisi metode iterator () , yang harus dikembalikan oleh Iterator.

Jika ada metode yang menerima Iterable di input, maka array tidak dapat ditransfer begitu saja. Meskipun Anda bisa mengulangi lebih dari satu array dalam for for loop, itu bukan Iterable .

 String[] someArray = new String[]{"a", "b", "c"}; for (String currentString : someArray) { ... } 

Dalam contoh ini, semuanya baik-baik saja. Tetapi jika ada metode:

 private static void someIteration(Iterable<String> iterable) { ... } 

Baris itu tidak akan dikompilasi:

 someIteration(someArray); 

Satu-satunya jalan keluar dalam situasi ini adalah dengan mengubah array ke koleksi dan sudah mengumpankannya ke metode seperti itu.

Secara singkat tentang beberapa metode Koleksi yang berguna


MetodeKomentar
maks (Koleksi) dan maks (Koleksi, Pembanding)
min (Koleksi) dan min (Koleksi, Pembanding)
Harap dicatat bahwa Anda dapat menerapkan input Pembanding
indexOfSubList (Daftar, Daftar)
Menemukan indeks kemunculan pertama dari satu daftar (argumen kedua) di daftar lain (argumen pertama)
lastIndexOfSubList (Daftar, Daftar)
Menemukan indeks kemunculan terakhir dari satu daftar (argumen kedua) di daftar lainnya (argumen pertama)
membalikkan (Daftar)
Susun ulang item dalam urutan terbalik

Apa yang layak dibaca?


Ini hanya sebagian kecil dari alat yang dapat membuat hidup lebih mudah bagi pengembang ketika bekerja dengan struktur data. Banyak poin menarik dalam karya koleksi itu sendiri dan alat yang mudah untuk bekerja dengannya dapat ditemukan dalam buku Bruce Eckel “Java Philosophy” (edisi penuh ke-4). Namun, Anda harus berhati-hati, karena menghadapi situasi yang tidak lagi dapat dimainkan di Java 7, Java 8 dan lebih tinggi. Meskipun Java 6 dijelaskan dalam buku ini, materinya tetap relevan hingga saat ini.

Tentu saja, “Filosofi Jawa” tidak boleh dibatasi. Membaca salah satu buku ini tidak akan merugikan pengembang Java mana pun:

  • "Jawa. Pemrograman yang Efektif, ”Joshua Bloch.
  • “Refactoring. Meningkatkan Desain Kode yang Ada, ”Martin Fowler.
  • “Kode bersih. Penciptaan, analisis, dan refactoring ”, Robert Martin.
  • Spring 5 untuk Profesional, Julian Kozmin dan lainnya.
  • “Pengembangan Java Berbasis Tes”, Viktor Farcic, Alex Garcia (belum dirilis dalam bahasa Rusia).

Apa hasilnya?


Jika Anda menemukan ide-ide menarik yang dapat melengkapi apa yang ditulis dalam artikel ini, bagikan dalam komentar.

Saya juga ingin mengucapkan semoga sukses dan kesabaran kepada mereka yang bekerja dengan kode lama yang lama. Sebagian besar proyek besar adalah warisan. Dan signifikansi mereka bagi pelanggan sulit ditaksir terlalu tinggi. Dan perasaan kemenangan dari menghilangkan bug, yang membutuhkan waktu lebih dari seminggu untuk menemukan alasannya, tidak kalah dengan perasaan pada akhir penerapan fitur baru.

Terima kasih atas perhatian anda Saya akan senang jika ada yang disajikan akan bermanfaat.
Semua sukses!

Source: https://habr.com/ru/post/id476098/


All Articles