JavaRush /Java Blog /Random-ID /Mari kita uraikan kelas StringUtils

Mari kita uraikan kelas StringUtils

Dipublikasikan di grup Random-ID
Halo semuanya, para pembaca yang budiman. Saya mencoba menulis tentang apa yang benar-benar menarik minat saya dan apa yang membuat saya khawatir saat ini. Oleh karena itu, hari ini akan ada beberapa bacaan ringan yang berguna bagi Anda sebagai referensi di kemudian hari: mari kita bahas tentang StringUtils . Mari kita uraikan kelas StringUtils - 1Kebetulan pada suatu waktu saya melewati perpustakaan Apache Commons Lang 3 . Ini adalah perpustakaan dengan kelas tambahan untuk bekerja dengan objek yang berbeda. Ini adalah kumpulan metode yang berguna untuk bekerja dengan string, koleksi, dan sebagainya. Pada proyek saat ini, di mana saya harus bekerja lebih detail dengan string dalam menerjemahkan logika bisnis berusia 25 tahun (dari COBOL ke Java), ternyata saya tidak memiliki pengetahuan yang cukup mendalam tentang kelas StringUtils . Jadi saya harus menciptakan semuanya sendiri. Apa yang saya maksud? Fakta bahwa Anda tidak perlu menulis sendiri tugas-tugas tertentu yang melibatkan manipulasi string, tetapi menggunakan solusi yang sudah jadi. Apa salahnya menulis sendiri? Setidaknya ini lebih banyak kode yang telah ditulis sejak lama. Yang tidak kalah mendesaknya adalah masalah pengujian kode yang ditulis tambahan. Ketika kita menggunakan perpustakaan yang telah terbukti bagus, kita berharap perpustakaan tersebut sudah diuji dan kita tidak perlu menulis banyak kasus uji untuk mengujinya. Kebetulan kumpulan metode untuk bekerja dengan string di Java tidak begitu banyak. Sebenarnya tidak banyak metode yang berguna untuk pekerjaan. Kelas ini juga dibuat untuk menyediakan pemeriksaan NullPointerException. Garis besar artikel kami adalah sebagai berikut:
  1. Bagaimana cara terhubung?
  2. Contoh dari pekerjaan saya: bagaimana, tanpa mengetahui tentang kelas yang bermanfaat, saya membuat kruk sepeda saya .
  3. Mari kita lihat metode lain yang menurut saya menarik.
  4. Mari kita rangkum.
Semua kasus akan ditambahkan ke repositori terpisah di organisasi Komunitas Javarush di GitHub. Akan ada contoh dan tes terpisah untuk mereka.

0. Bagaimana cara menghubungkan

Mereka yang berjalan beriringan dengan saya kurang lebih sudah familiar dengan Git dan Maven, jadi selanjutnya saya akan mengandalkan pengetahuan ini dan tidak mengulanginya sendiri. Bagi yang ketinggalan artikel saya sebelumnya atau baru mulai membaca, berikut materi tentang Maven dan Git . Tentu saja, tanpa sistem build (Maven, Gredl), Anda juga dapat menghubungkan semuanya secara manual, tetapi ini gila saat ini dan Anda pasti tidak perlu melakukannya seperti itu: lebih baik segera pelajari cara melakukan semuanya dengan benar. Oleh karena itu, untuk bekerja dengan Maven, pertama-tama kita menambahkan ketergantungan yang sesuai:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Dimana ${apache.common.version} adalah versi perpustakaan ini. Selanjutnya, untuk mengimpor di beberapa kelas, tambahkan import:
import org.apache.commons.lang3.StringUtils;
Dan itu saja, semuanya ada di dalam tas))

1. Contoh dari proyek nyata

  • metode papan kiri

Contoh pertama secara umum tampak sangat bodoh sekarang karena sangat baik jika rekan saya mengetahui tentang StringUtils.leftPad dan memberi tahu saya. Apa tugasnya: kode dibuat sedemikian rupa sehingga data perlu diubah jika tidak sampai dengan benar. Bidang string diharapkan hanya terdiri dari angka, mis. jika panjangnya 3 dan nilainya 1, maka entrinya harus “001”. Artinya, pertama-tama Anda harus menghapus semua spasi, lalu menutupinya dengan nol. Lebih banyak contoh untuk memperjelas inti tugas: dari “12” -> “012” dari “1” -> “001” Dan seterusnya. Apa yang saya lakukan? Menjelaskan hal ini di kelas LeftPadExample . Saya menulis sebuah metode yang akan melakukan semua ini:
public static String ownLeftPad(String value) {
   String trimmedValue = value.trim();

   if(trimmedValue.length() == value.length()) {
       return value;
   }

   StringBuilder newValue = new StringBuilder(trimmedValue);

   IntStream.rangeClosed(1, value.length() - trimmedValue.length())
           .forEach(it -> newValue.insert(0, "0"));
   return newValue.toString();
}
Sebagai dasar, saya mengambil gagasan bahwa kita bisa mendapatkan perbedaan antara nilai asli dan nilai yang dipangkas dan mengisinya dengan angka nol di depannya. Untuk melakukan ini saya menggunakan IntStream untuk melakukan operasi yang sama sebanyak n kali. Dan ini tentu perlu diuji. Inilah yang bisa saya lakukan jika saya sudah mengetahui tentang metode StringUtils.leftPad sebelumnya :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Seperti yang Anda lihat, kodenya jauh lebih sedikit, dan perpustakaan yang dikonfirmasi oleh semua orang juga digunakan. Untuk tujuan ini, saya membuat dua tes di kelas LeftPadExampleTest (biasanya ketika mereka berencana untuk menguji suatu kelas, mereka membuat kelas dengan nama yang sama + Test dalam paket yang sama, hanya di src/test/java). Pengujian ini memeriksa satu metode untuk memastikan bahwa metode tersebut mentransformasikan nilai dengan benar, lalu metode lainnya. Tentu saja, lebih banyak pengujian yang perlu ditulis, namun pengujian bukanlah topik utama dalam kasus kami:
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Unit-level testing for LeftPadExample")
class LeftPadExampleTest {

   @DisplayName("Should transform by using ownLeftPad method as expected")
   @Test
   public void shouldTransformOwnLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.ownLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

}
Saya dapat memberikan beberapa komentar tentang tes saat ini. Mereka ditulis menggunakan JUnit 5:
  1. Sebuah tes akan dianggap sebagai tes jika memiliki anotasi yang sesuai - @Test.
  2. Jika sulit mendeskripsikan pengoperasian pengujian pada namanya atau deskripsinya panjang dan tidak nyaman untuk dibaca, Anda dapat menambahkan anotasi @DisplayName dan menjadikannya deskripsi normal yang akan terlihat saat menjalankan pengujian.
  3. Saat menulis tes, saya menggunakan pendekatan BDD, di mana saya membagi tes menjadi beberapa bagian logis:
    1. //diberikan - blok pengaturan data sebelum pengujian;
    2. //kapan blok tempat bagian kode yang kita uji diluncurkan;
    3. //then adalah sebuah blok dimana hasil dari blok kapan diperiksa.
Jika Anda menjalankannya, mereka akan mengonfirmasi bahwa semuanya berfungsi sesuai harapan.

  • metode stripStart

Di sini saya perlu menyelesaikan masalah dengan garis yang mungkin memiliki spasi dan koma di awal. Setelah transformasi, mereka seharusnya tidak memiliki arti baru. Pernyataan masalahnya lebih jelas dari sebelumnya. Beberapa contoh akan memperkuat pemahaman kita: “, , books” -> “books” “,,, books” -> “books” b , books” -> “b , books” Seperti halnya dengan leftPad, saya menambahkan Kelas StrimStartExample , yang memiliki dua metode. Satu - dengan solusinya sendiri:
public static String ownStripStart(String value) {
   int index = 0;
   List commaSpace = asList(" ", ",");
   for (int i = 0; i < value.length(); i++) {
       if (commaSpace.contains(String.valueOf(value.charAt(i)))) {
           index++;
       } else {
           break;
       }
   }
   return value.substring(index);
}
Di sini idenya adalah untuk menemukan indeks mulai dari yang tidak ada lagi spasi atau koma. Jika mereka tidak ada sama sekali pada awalnya, maka indeksnya akan menjadi nol. Dan yang kedua - dengan solusi melalui StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Di sini kita meneruskan informasi argumen pertama tentang string mana yang sedang kita kerjakan, dan yang kedua kita meneruskan string yang terdiri dari karakter yang perlu dilewati. Kami membuat kelas StripStartExampleTest dengan cara yang sama :
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Unit-level testing for StripStartExample")
class StripStartExampleTest {

   @DisplayName("Should transform by using stripStart method as expected")
   @Test
   public void shouldTransformOwnStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.ownStripStart(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }
}

  • metode isEmpty

Cara ini tentu saja jauh lebih sederhana, namun tidak mengurangi manfaatnya. Ini memperluas kemampuan metode String.isEmpty() , yang juga menambahkan tanda centang untuk null. Untuk apa? Untuk menghindari NullPointerException, yaitu menghindari pemanggilan metode pada variabel yang null . Oleh karena itu, agar tidak menulis:
if(value != null && value.isEmpty()) {
   //doing something
}
Anda cukup melakukan ini:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Kelebihan metode ini adalah langsung terlihat jelas metode mana yang digunakan.

2. Analisis metode lain dari kelas StringUtils

Sekarang mari kita bicara tentang metode-metode yang menurut saya juga patut mendapat perhatian. Berbicara secara umum tentang StringUtils , perlu dikatakan bahwa ia menyediakan metode null aman yang serupa dengan yang ditemukan di kelas String (seperti halnya dengan metode isEmpty ). Mari kita bahas:

  • membandingkan metode

Metode seperti itu ada di String dan akan memunculkan NullPointerException jika, saat membandingkan dua string, salah satunya adalah null. Untuk menghindari pemeriksaan buruk pada kode kita, kita dapat menggunakan metode StringUtils.compare(String str1, String str2) : metode ini mengembalikan int sebagai hasil perbandingan. Apa arti nilai-nilai ini? int = 0 jika keduanya sama (atau keduanya null). int < 0, jika str1 lebih kecil dari str2. int > 0, jika str1 lebih besar dari str2. Selain itu, jika Anda melihat dokumentasinya, Javadoc metode ini menyajikan skenario berikut:
StringUtils.compare(null, null)   = 0
StringUtils.compare(null , "a")   < 0
StringUtils.compare("a", null)    > 0
StringUtils.compare("abc", "abc") = 0
StringUtils.compare("a", "b")     < 0
StringUtils.compare("b", "a")     > 0
StringUtils.compare("a", "B")     > 0
StringUtils.compare("ab", "abc")  < 0

  • berisi... metode

Di sini para pengembang utilitas bersenang-senang. Metode apa pun yang Anda inginkan ada di sana. Saya memutuskan untuk menggabungkannya:
  1. berisi adalah metode yang memeriksa apakah string yang diharapkan ada di dalam string lain. Apa manfaatnya? Anda dapat menggunakan metode ini jika Anda ingin memastikan bahwa ada kata tertentu dalam teks.

    Contoh:

    StringUtils.contains(null, *)     = false
    StringUtils.contains(*, null)     = false
    StringUtils.contains("", "")      = true
    StringUtils.contains("abc", "")   = true
    StringUtils.contains("abc", "a")  = true
    StringUtils.contains("abc", "z")  = false

    Sekali lagi, keamanan NPE (Null Pointer Exception) hadir.

  2. berisiAny adalah metode yang memeriksa apakah ada karakter yang ada dalam string. Juga hal yang berguna: Anda harus sering melakukan ini.

    Contoh dari dokumentasi:

    StringUtils.containsAny(null, *)                  = false
    StringUtils.containsAny("", *)                    = false
    StringUtils.containsAny(*, null)                  = false
    StringUtils.containsAny(*, [])                    = false
    StringUtils.containsAny("zzabyycdxx", ['z', 'a']) = true
    StringUtils.containsAny("zzabyycdxx", ['b', 'y']) = true
    StringUtils.containsAny("zzabyycdxx", ['z', 'y']) = true
    StringUtils.containsAny("aba", ['z'])             = false

  3. berisiIgnoreCase adalah ekstensi yang berguna untuk metode berisi . Memang, untuk memeriksa kasus seperti itu tanpa metode ini, Anda harus melalui beberapa opsi. Jadi hanya satu metode yang akan digunakan secara harmonis.

  4. Beberapa contoh dari dokumen:

    StringUtils.containsIgnoreCase(null, *) = false
    StringUtils.containsIgnoreCase(*, null) = false
    StringUtils.containsIgnoreCase("", "") = true
    StringUtils.containsIgnoreCase("abc", "") = true
    StringUtils.containsIgnoreCase("abc", "a") = true
    StringUtils.containsIgnoreCase("abc", "z") = false
    StringUtils.containsIgnoreCase("abc", "A") = true
    StringUtils.containsIgnoreCase("abc", "Z") = false

  5. berisiNone - dilihat dari namanya, Anda sudah dapat memahami apa yang sedang diperiksa. Seharusnya tidak ada garis di dalamnya. Suatu hal yang bermanfaat tentunya. Pencarian cepat untuk beberapa karakter yang tidak diinginkan;). Di bot telegram kami , kami akan memfilter kata-kata kotor dan tidak akan mengabaikan metode lucu ini.

    Dan contoh bagaimana jadinya kita tanpa mereka:

    StringUtils.containsNone(null, *)       = true
    StringUtils.containsNone(*, null)       = true
    StringUtils.containsNone("", *)         = true
    StringUtils.containsNone("ab", '')      = true
    StringUtils.containsNone("abab", 'xyz') = true
    StringUtils.containsNone("ab1", 'xyz')  = true
    StringUtils.containsNone("abz", 'xyz')  = false

  • metode string default

Serangkaian metode yang membantu menghindari penambahan informasi tambahan jika stringnya null dan Anda perlu menetapkan beberapa nilai default. Ada banyak pilihan untuk memenuhi setiap selera. Yang paling utama di antaranya adalah StringUtils.defaultString(final String str, final String defaultStr) - jika str adalah null, kami cukup meneruskan nilainya ke defaultStr . Contoh dari dokumentasi:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Sangat mudah digunakan saat Anda membuat kelas POJO dengan data.

  • hapus metode spasi putih

Ini adalah metode yang menarik, meskipun tidak banyak pilihan untuk penerapannya. Pada saat yang sama, jika kasus seperti itu muncul, metode ini pasti akan sangat berguna. Ini menghapus semua spasi dari string. Dimanapun celah ini berada, tidak akan ada jejaknya))) Contoh dari dokumen:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • berakhirDengan metode

Berbicara untuk dirinya sendiri. Ini adalah metode yang sangat berguna: ia memeriksa apakah string diakhiri dengan string yang disarankan atau tidak. Hal ini seringkali diperlukan. Tentu saja, Anda dapat menulis ceknya sendiri, tetapi menggunakan metode yang sudah jadi jelas lebih nyaman dan lebih baik. Contoh:
StringUtils.endsWith(null, null)      = true
StringUtils.endsWith(null, "def")     = false
StringUtils.endsWith("abcdef", null)  = false
StringUtils.endsWith("abcdef", "def") = true
StringUtils.endsWith("ABCDEF", "def") = false
StringUtils.endsWith("ABCDEF", "cde") = false
StringUtils.endsWith("ABCDEF", "")    = true
Seperti yang Anda lihat, semuanya diakhiri dengan baris kosong))) Saya pikir contoh ini (StringUtils.endsWith("ABCDEF", "") = true) hanyalah bonus, karena ini tidak masuk akal) Ada juga metode yang mengabaikan kasus.

  • sama dengan metode

Contoh bagus dari metode null safe yang membandingkan dua string. Apapun yang kita masukkan ke dalamnya, jawabannya akan ada di sana, dan tanpa kesalahan. Contoh:
StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false
Tentu saja, ada juga equalIgnoreCase - semuanya dilakukan dengan cara yang persis sama, hanya saja kita mengabaikan kasus ini. Mari kita lihat?
StringUtils.equalsIgnoreCase(null, null)   = true
StringUtils.equalsIgnoreCase(null, "abc")  = false
StringUtils.equalsIgnoreCase("abc", null)  = false
StringUtils.equalsIgnoreCase("abc", "abc") = true
StringUtils.equalsIgnoreCase("abc", "ABC") = true

  • sama dengan metode apa pun

Mari lanjutkan dan kembangkan metode yang sama . Katakanlah alih-alih melakukan beberapa pemeriksaan kesetaraan, kita ingin melakukan satu pemeriksaan kesetaraan. Untuk ini, kita dapat meneruskan sebuah string yang dengannya sekumpulan string akan dibandingkan; jika salah satu dari string tersebut sama dengan yang diusulkan, maka itu akan menjadi BENAR. Kami meneruskan sebuah string dan kumpulan string untuk membandingkannya satu sama lain (string pertama dengan string dari koleksi). Sulit? Berikut ini contoh dari dokumen untuk membantu Anda memahami maksudnya:
StringUtils.equalsAny(null, (CharSequence[]) null) = false
StringUtils.equalsAny(null, null, null)    = true
StringUtils.equalsAny(null, "abc", "def")  = false
StringUtils.equalsAny("abc", null, "def")  = false
StringUtils.equalsAny("abc", "abc", "def") = true
StringUtils.equalsAny("abc", "ABC", "DEF") = false
Ada juga yang sama denganAnyIgnoreCase . Dan contohnya:
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true

Intinya

Sebagai hasilnya, kita pulang dengan pengetahuan tentang apa itu StringUtils dan metode berguna apa yang dimilikinya. Nah, dengan kesadaran bahwa ada hal-hal yang berguna dan tidak perlu selalu memagari dengan kruk di tempat-tempat yang memungkinkan untuk menutup masalah dengan bantuan solusi yang sudah jadi. Secara umum, kami hanya menganalisis sebagian metode. Jika Anda mau, saya bisa melanjutkan: masih banyak lagi, dan memang patut mendapat perhatian. Jika Anda mempunyai ide tentang bagaimana hal ini dapat disajikan, silakan tulis - Saya selalu terbuka untuk ide-ide baru. Dokumentasi metode ditulis dengan sangat baik, contoh pengujian dengan hasil ditambahkan, yang membantu untuk lebih memahami pengoperasian metode. Oleh karena itu, kami tidak segan-segan membaca dokumentasinya: ini akan menghilangkan keraguan Anda tentang fungsi utilitas tersebut. Untuk mendapatkan pengalaman pengkodean baru, saya menyarankan Anda untuk melihat bagaimana kelas utilitas dibuat dan ditulis. Ini akan berguna di masa depan, karena biasanya setiap proyek memiliki kelas memo sendiri, dan pengalaman menulisnya akan berguna. Secara tradisional, saya sarankan Anda berlangganan akun saya di Github ) Bagi yang belum tahu tentang proyek saya dengan bot telegram, berikut ini tautan ke artikel pertama . Terima kasih semuanya telah membaca. Saya telah menambahkan beberapa tautan berguna di bawah.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION