JavaRush /Java blogi /Random-UZ /Keling, StringUtils sinfini ajratamiz

Keling, StringUtils sinfini ajratamiz

Guruhda nashr etilgan
Hammaga salom, aziz o'quvchilarim. Meni haqiqatan nima qiziqtirayotgani va ayni damda nimalar tashvishlantirayotgani haqida yozishga harakat qilaman. Shuning uchun, bugungi kunda siz uchun kelajakda ma'lumotnoma sifatida foydali bo'ladigan engil o'qishlar bo'ladi: keling, StringUtils haqida gapiraylik . Keling, StringUtils sinfini ajratamiz - 1Shunday bo'ldiki, bir vaqtlar men Apache Commons Lang 3 kutubxonasini chetlab o'tganman . Bu turli ob'ektlar bilan ishlash uchun yordamchi sinflarga ega kutubxona. Bu satrlar, to'plamlar va boshqalar bilan ishlash uchun foydali usullar to'plamidir. 25 yillik biznes mantig'ini (COBOL'dan Java'ga) tarjima qilishda qatorlar bilan batafsilroq ishlashga to'g'ri kelgan joriy loyihada men StringUtils sinfi haqida etarlicha chuqur bilimga ega emasligim ma'lum bo'ldi . Shuning uchun men hamma narsani o'zim yaratishim kerak edi. Nima demoqchiman? Gap shundaki, siz string manipulyatsiyasi bilan bog'liq ba'zi vazifalarni o'zingiz yozishingiz shart emas, lekin tayyor echimdan foydalaning. O'zingiz yozganingizning nimasi yomon? Hech bo'lmaganda, bu uzoq vaqt oldin yozilgan ko'proq koddir. Qo'shimcha ravishda yozilgan kodni sinab ko'rish masalasi ham dolzarbdir. O'zini yaxshi isbotlagan kutubxonadan foydalanganda, biz u allaqachon sinovdan o'tgan va uni sinab ko'rish uchun bir nechta test holatlarini yozishimiz shart emasligini kutamiz. Java-da satr bilan ishlash usullari to'plami unchalik katta emasligi shunday bo'ladi. Ish uchun foydali bo'lgan usullar juda ko'p emas. Ushbu sinf NullPointerException uchun tekshiruvlarni ta'minlash uchun ham yaratilgan. Maqolamizning mazmuni quyidagicha bo'ladi:
  1. Qanday ulanish kerak?
  2. Mening ishimdan misollar: qanday qilib bunday foydali sinf haqida bilmagan holda, men velosiped tayoqchamni yaratdim.
  3. Keling, men uchun qiziqarli bo'lgan boshqa usullarni ko'rib chiqaylik.
  4. Keling, xulosa qilaylik.
Barcha holatlar GitHub-dagi Javarush jamoat tashkilotidagi alohida omborga qo'shiladi . Ular uchun alohida misollar va testlar bo'ladi.

0. Qanday ulanish kerak

Men bilan qo'l qovushtirib yurganlar Git va Maven bilan ko'proq yoki kamroq tanish, shuning uchun men bu bilimga tayanaman va o'zimni takrorlamayman. Oldingi maqolalarimni o'tkazib yuborgan yoki endigina o'qishni boshlaganlar uchun bu erda Maven va Git haqidagi materiallar mavjud . Albatta, qurish tizimisiz (Maven, Gredl) siz hamma narsani qo'lda ham ulashingiz mumkin, ammo bu bugungi kunda aqldan ozgan va buni qilishning hojati yo'q: hamma narsani qanday qilib to'g'ri qilishni darhol o'rganish yaxshiroqdir. Shuning uchun, Maven bilan ishlash uchun avvalo tegishli bog'liqlikni qo'shamiz:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
${apache.common.version} bu kutubxonaning versiyasi . Keyin, ba'zi sinfga import qilish uchun import qo'shing:
import org.apache.commons.lang3.StringUtils;
Va bu, hammasi sumkada))

1. Haqiqiy loyihadan misollar

  • leftPad usuli

Birinchi misol, umuman olganda, hozir juda ahmoqona ko'rinadi, chunki mening hamkasblarim StringUtils.leftPad haqida bilishi va menga aytgani juda yaxshi. Vazifa nima edi: kod shunday tuzilganki, agar ma'lumotlar to'g'ri kelmasa, uni o'zgartirish kerak edi. Satr maydoni faqat raqamlardan iborat bo'lishi kutilgan edi, ya'ni. agar uning uzunligi 3 bo'lsa va uning qiymati 1 bo'lsa, u holda yozuv "001" bo'lishi kerak. Ya'ni, avval siz barcha bo'shliqlarni olib tashlashingiz kerak, so'ngra uni nol bilan qoplashingiz kerak. Vazifaning mohiyatini aniqroq qilish uchun ko'proq misollar: "12" dan -> "012" dan "1" -> "001" va hokazo. Men nima qildim? Bu LeftPadExample sinfida tasvirlangan . Bularning barchasini bajaradigan usulni yozdim:
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();
}
Asl va qisqartirilgan qiymat o'rtasidagi farqni oddiygina olishimiz va uni oldingi nol bilan to'ldirishimiz mumkin degan fikrni asos qilib oldim. Buni amalga oshirish uchun men IntStream-dan bir xil operatsiyani n marta bajarish uchun foydalandim . Va bu, albatta, sinovdan o'tishi kerak. Agar StringUtils.leftPad usuli haqida oldindan bilganimda nima qila olardim :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Ko'rib turganingizdek, kod juda kam va hamma tomonidan tasdiqlangan kutubxona ham ishlatiladi. Shu maqsadda men LeftPadExampleTest sinfida ikkita test yaratdim (odatda ular sinfni sinab ko'rishni rejalashtirganda, ular bir xil nomli sinf yaratadilar + Test bir xil paketda, faqat src/test/java da). Ushbu testlar qiymatni to'g'ri o'zgartirishiga ishonch hosil qilish uchun bir usulni, keyin boshqasini tekshiradi. Albatta, ko'proq testlarni yozish kerak bo'ladi, ammo test bizning holatlarimizda asosiy mavzu emas:
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);
   }

}
Hozircha testlar haqida bir nechta sharhlar bera olaman. Ular JUnit 5 yordamida yozilgan:
  1. Agar test tegishli izohga ega bo'lsa, test sinov sifatida ko'rib chiqiladi - @Test.
  2. Agar nom testning ishlashini tasvirlash qiyin bo'lsa yoki tavsif uzoq va o'qish uchun noqulay bo'lsa, siz @DisplayName izohini qo'shishingiz va uni testlarni o'tkazishda ko'rinadigan oddiy tavsifga aylantirishingiz mumkin.
  3. Testlarni yozishda men BDD yondashuvidan foydalanaman, unda testlarni mantiqiy qismlarga ajrataman:
    1. //berilgan - testdan oldin ma'lumotlarni o'rnatish bloki;
    2. //biz tekshirayotgan kod qismi ishga tushiriladigan blok qachon;
    3. //keyin blok bo'lib, unda when blokining natijalari tekshiriladi.
Agar siz ularni ishga tushirsangiz, ular hamma narsa kutilganidek ishlayotganini tasdiqlaydi.

  • stripStart usuli

Bu erda men boshida bo'sh joy va vergul bo'lishi mumkin bo'lgan chiziq bilan muammoni hal qilishim kerak edi. Transformatsiyadan keyin ular yangi ma'noga ega bo'lmasligi kerak edi. Muammo bayonoti har qachongidan ham aniqroq. Bir nechta misollar tushunchamizni mustahkamlaydi: “, , kitoblar” -> “kitoblar” “,,, kitoblar” -> “kitoblar” b , kitoblar” -> “b , kitoblar” leftPad bilan bo'lgani kabi, men qo'shdim StrimStartExample klassi , unda ikkita usul mavjud. Biri - o'z yechimi bilan:
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);
}
Bu erda bo'sh joy yoki vergul qo'yilmaydigan indeksni topish g'oyasi edi. Agar ular boshida umuman bo'lmagan bo'lsa, unda indeks nolga teng bo'ladi. Va ikkinchisi - StringUtils orqali yechim bilan :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Bu erda biz qaysi qator bilan ishlayotganimiz haqidagi birinchi argument ma'lumotlarini, ikkinchisida esa o'tkazib yuborish kerak bo'lgan belgilardan iborat qatorni o'tkazamiz. StripStartExampleTest sinfini xuddi shu tarzda yaratamiz :
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);
   }
}

  • isEmpty usuli

Bu usul, albatta, ancha sodda, ammo bu uni kamroq foydali qilmaydi. U String.isEmpty() usulining imkoniyatlarini kengaytiradi , bu esa null uchun chek qo'shadi. Nima uchun? NullPointerException dan qochish uchun, ya'ni null bo'lgan o'zgaruvchida usullarni chaqirmaslik uchun . Shuning uchun, yozmaslik uchun:
if(value != null && value.isEmpty()) {
   //doing something
}
Buni oddiygina qilishingiz mumkin:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Ushbu usulning afzalligi shundaki, qaysi usuldan foydalanilganligi darhol aniq bo'ladi.

2. StringUtils sinfining boshqa usullarini tahlil qilish

Endi, mening fikrimcha, e'tiborga loyiq bo'lgan usullar haqida gapiraylik. Umuman StringUtils haqida gapiradigan bo'lsak, shuni aytish kerakki, u String sinfida topilganlarga o'xshash null xavfsiz usullarni taqdim etadi ( isEmpty usulida bo'lgani kabi ). Keling, ularni ko'rib chiqaylik:

  • solishtirish usuli

Bunday usul String da mavjud va agar ikkita satrni solishtirganda ulardan biri null bo'lsa, NullPointerException ni chiqaradi. Kodimizda xunuk tekshiruvlardan qochish uchun biz StringUtils.compare(String str1, String str2) usulidan foydalanishimiz mumkin : u taqqoslash natijasida intni qaytaradi. Bu qadriyatlar nimani anglatadi? int = 0, agar ular bir xil bo'lsa (yoki ikkalasi ham null bo'lsa). int < 0, agar str1 str2 dan kichik bo'lsa. int > 0, agar str1 str2 dan katta bo'lsa. Bundan tashqari, agar siz ularning hujjatlariga qarasangiz, ushbu usulning Javadoc dasturi quyidagi stsenariylarni taqdim etadi:
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

  • ... usullarini o‘z ichiga oladi

Bu erda kommunal dastur ishlab chiquvchilari juda xursand bo'lishdi. Siz xohlagan usul mavjud. Men ularni birlashtirishga qaror qildim:
  1. contain - bu kutilgan satrning boshqa satr ichida ekanligini tekshiradigan usul. Bu qanday foydali? Agar matnda ma'lum bir so'z borligiga ishonch hosil qilishingiz kerak bo'lsa, ushbu usuldan foydalanishingiz mumkin.

    Misollar:

    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

    Shunga qaramay, NPE (Null Pointer Exception) xavfsizligi mavjud.

  2. containAny - bu satrda mavjud bo'lgan belgilar mavjudligini tekshiradigan usul. Bundan tashqari, foydali narsa: buni tez-tez qilishingiz kerak.

    Hujjatlardan misollar:

    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. containIgnoreCase contain usulining foydali kengaytmasidir . Darhaqiqat, bunday ishni ushbu usulsiz tekshirish uchun siz bir nechta variantlardan o'tishingiz kerak bo'ladi. Va shuning uchun faqat bitta usul uyg'un ravishda qo'llaniladi.

  4. Hujjatlardan bir nechta misollar:

    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. containNone - nomiga qarab, siz allaqachon tekshirilayotgan narsani tushunishingiz mumkin. Ichkarida chiziqlar bo'lmasligi kerak. Foydali narsa, albatta. Ba'zi keraksiz belgilarni tezda qidirish;). Telegram botimizda odobsizliklarni filtrlaymiz va bu kulgili usullarni e'tiborsiz qoldirmaymiz.

    Va misollar, ularsiz qayerda bo'lar edik:

    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

  • defaultString usuli

Agar satr null bo'lsa va siz ba'zi bir standart qiymatni o'rnatishingiz kerak bo'lsa, qo'shimcha ma'lumot qo'shmaslikka yordam beradigan bir qator usullar. Har qanday ta'mga mos keladigan ko'plab variantlar mavjud. Ulardan asosiysi StringUtils.defaultString(yakuniy String str, final String defaultStr) - agar str null bo'lsa, biz shunchaki defaultStr qiymatini o'tkazamiz . Hujjatlardan misollar:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Ma'lumotlar bilan POJO sinfini yaratishda foydalanish juda qulay.

  • deleteWhitespace usuli

Bu qiziqarli usul, garchi uni qo'llashning ko'p variantlari mavjud emas. Shu bilan birga, agar bunday holat yuzaga kelsa, usul, albatta, juda foydali bo'ladi. U satrdagi barcha bo'shliqlarni olib tashlaydi. Bu bo'shliq qayerda bo'lmasin, undan asar ham qolmaydi))) Hujjatlardan misollar:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • endsWith usuli

O'zi uchun gapiradi. Bu juda foydali usul: u satr tavsiya etilgan satr bilan tugaydimi yoki yo'qligini tekshiradi. Bu ko'pincha kerak. Albatta, siz chekni o'zingiz yozishingiz mumkin, ammo tayyor usuldan foydalanish yanada qulayroq va yaxshiroq. Misollar:
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
Ko'rib turganingizdek, hamma narsa bo'sh satr bilan tugaydi))) Menimcha, bu misol (StringUtils.endsWith("ABCDEF", "") = rost) shunchaki bonus, chunki bu bema'nilik) Yana bir usul bor holatga e'tibor bermaydi.

  • teng usuli

Ikki qatorni taqqoslaydigan null xavfsiz usulning ajoyib namunasi. U erga nima qo'ysak, javob o'sha erda bo'ladi va u xatosiz bo'ladi. Misollar:
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
Albatta, equalsIgnoreCase ham mavjud - hamma narsa xuddi shu tarzda amalga oshiriladi, faqat biz ishni e'tiborsiz qoldiramiz. Ko'raylikchi?
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

  • teng har qanday usul

Keling, tenglash usulini kengaytiramiz . Aytaylik, bir nechta tenglikni tekshirish o'rniga biz bittasini o'tkazmoqchimiz. Buning uchun biz satrlar to'plamini solishtiradigan satrni o'tkazishimiz mumkin, agar ulardan birontasi taklif qilinganiga teng bo'lsa, u TRUE bo'ladi. Biz ularni bir-biri bilan solishtirish uchun satr va satrlar to'plamini o'tkazamiz (to'plamdagi satrlar bilan birinchi qator). Qiyinmi? Bu erda nima nazarda tutilganini tushunishga yordam beradigan hujjatlardan misollar:
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
Shuningdek, equalsAnyIgnoreCase mavjud . Va bunga misollar:
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

Pastki chiziq

Natijada, biz StringUtils nima ekanligini va uning qanday foydali usullari borligini bilib olamiz. Xo'sh, bunday foydali narsalar borligini va har safar muammoni tayyor echim yordamida yopish mumkin bo'lgan joylarda qo'ltiq tayoqchalari bilan o'rashning hojati yo'qligini anglash bilan. Umuman olganda, biz usullarning faqat bir qismini tahlil qildik. Agar xohlasangiz, davom eta olaman: ularning ko'plari bor va ular haqiqatan ham e'tiborga loyiqdir. Agar sizda buni qanday qilib taqdim etish mumkinligi haqida biron bir fikringiz bo'lsa, yozing - men har doim yangi g'oyalarga ochiqman. Usullar uchun hujjatlar juda yaxshi yozilgan, natijalar bilan test misollari qo'shilgan, bu usulning ishlashini yaxshiroq tushunishga yordam beradi. Shuning uchun biz hujjatlarni o'qishdan qochmaymiz: bu yordam dasturining funksionalligi haqidagi shubhalaringizni yo'q qiladi. Yangi kodlash tajribasiga ega bo'lish uchun sizga foydali sinflar qanday yaratilgani va yozilishini ko'rib chiqishni maslahat beraman. Bu kelajakda foydali bo'ladi, chunki odatda har bir loyihaning o'z hurda sinflari bor va ularni yozish tajribasi foydali bo'ladi. An'anaga ko'ra, Github'dagi akkauntimga obuna bo'lishingizni taklif qilaman ) Telegram bot bilan loyiham haqida bilmaganlar uchun bu erda birinchi maqolaga havola . O'qiganingiz uchun barchaga rahmat. Men quyida foydali havolalarni qo'shdim.
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION