JavaRush /Java блогу /Random-KY /Келгиле, StringUtils классын талкалап көрөлү
Roman Beekeeper
Деңгээл

Келгиле, StringUtils классын талкалап көрөлү

Группада жарыяланган
Баарыңарга салам, менин сүйүктүү окурмандарым. Мени чындап кызыктырган жана учурда мени эмне тынчсыздандырганы тууралуу жазууга аракет кылам. Ошондуктан, бүгүн сиз үчүн келечекте шилтеме катары пайдалуу боло турган бир нече жеңил окуу болот: келгиле, StringUtils жөнүндө сүйлөшөлү . Келгиле, StringUtils классын бөлүп көрөлү - 1Бир убакта мен Apache Commons Lang 3 китепканасын кыйгап өтүп кеттим . Бул ар кандай an objectтер менен иштөө үчүн көмөкчү класстары бар китепкана. Бул саптар, коллекциялар жана башкалар менен иштөө үчүн пайдалуу ыкмалардын жыйындысы. Учурдагы долбоордо, мен 25 жылдык бизнес логикасын (COBOLдан Javaга чейин) которууда саптар менен деталдуу иштешим керек болчу, мен StringUtils классы боюнча терең бorмге ээ эмес экенмин . Ошондуктан баарын өзүм жаратууга туура келди. Мен эмнени айткым келет? Сап менен иштөөнү камтыган айрым тапшырмаларды өзүңүз жазуунун кереги жок, бирок даяр чечимди колдонуңуз. Өзүң жазгандын эмнеси жаман? Жок дегенде, бул көп убакыт мурун жазылган code. Кошумча жазылган codeду сыноо маселеси дагы актуалдуу. Биз өзүн жакшы деп далилдеген китепкананы колдонгондо, ал буга чейин эле сыналган деп күтөбүз жана аны сынап көрүү үчүн бир топ сыноо иштерин жазуунун кереги жок. Javaдагы сап менен иштөө ыкмаларынын жыйындысы анчалык деле чоң эмес. Иш үчүн пайдалуу боло турган ыкмалар чынында эле көп эмес. Бул класс ошондой эле NullPointerException үчүн текшерүүлөрдү камсыз кылуу үчүн түзүлгөн. Биздин макаланын схемасы төмөнкүдөй болот:
  1. Кантип туташса болот?
  2. Менин ишимден мисалдар: кантип, ушундай пайдалуу класс жөнүндө билбей туруп, мен велосипед балдакымды жараттым.
  3. Мага кызыктуу болгон башка ыкмаларды карап көрөлү.
  4. Жыйынтыктап көрөлү.
Бардык учурлар GitHubдагы Javarush Community уюмунун өзүнчө репозиторийине кошулат . Алар үчүн өзүнчө мисалдар жана тесттер болот.

0. Кантип туташуу керек

Мени менен кол кармашып жүргөндөр Гит жана Мавен экөөн тең аздыр-көптүр бorшет, ошондуктан мен мындан ары бул бorмге таянам жана өзүмдү кайталабайм. Менин мурунку макалаларымды сагынган же жаңы эле окуп баштагандар үчүн бул жерде Maven жана Git жөнүндө материалдар бар . Албетте, куруу системасы жок (Maven, Gredl) сиз баарын кол менен туташтыра аласыз, бирок бул бүгүнкү күндө жинди жана муну сөзсүз түрдө жасоонун кереги жок: баарын кантип туура жасоону дароо үйрөнүп алганыңыз жакшы. Ошондуктан, Maven менен иштөө үчүн, адегенде тиешелүү көз карандылыкты кошобуз:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
${apache.common.version} бул китепкананын versionсы . Андан кийин, кандайдыр бир класста импорттоо үчүн импортту кошуңуз:
import org.apache.commons.lang3.StringUtils;
Болду, баары баштыкта))

1. Чыныгы долбоордон мисалдар

  • leftPad ыкмасы

Биринчи мисал жалпысынан ушунчалык акылсыз окшойт, менин кесиптештерим StringUtils.leftPad жөнүндө бorп, мага айтып беришкени абдан жакшы . Тапшырма кандай болгон: code, эгерде ал туура келбесе, маалыматтарды трансформациялоо керек болчудай курулган. Сап талаасы сандардан гана турушу керек деп күтүлгөн, б.а. анын узундугу 3 жана анын мааниси 1 болсо, анда жазуу "001" болушу керек. Башкача айтканда, алгач бардык боштуктарды алып салуу керек, андан кийин нөл менен жабуу керек. Тапшырманын маңызын ачыктоо үчүн дагы мисалдар: “12“ ден -> “012”ден “1“ -> “001” ж.б.у.с. Мен эмне кылдым? Муну LeftPadExample классында сүрөттөгөн . Мен мунун баарын жасай турган ыкманы жаздым:
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();
}
Негиз катары, мен жөн гана түпнуска менен кесилген маанинин ортосундагы айырманы алып, аны алдына нөл менен толтурсак болот деген ойду алдым. Бул үчүн мен бир эле операцияны n жолу жасоо үчүн IntStreamди колдондум. Жана бул сөзсүз түрдө сыналышы керек. Эгерде мен StringUtils.leftPad ыкмасы жөнүндө алдын ала билгенимде эмне кылмакмын :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Көрүнүп тургандай, бир топ аз code бар жана ар бир адам тарабынан тастыкталган китепкана да колдонулат. Ушул максатта, мен LeftPadExampleTest классында эки тест түздүм (көбүнчө алар классты сынап көрүүнү пландаштырганда, алар src/test/javaда гана бирдей аталыштагы классты + Ошол эле пакетте Test түзүшөт). Бул тесттер бир ыкманы текшерип, анын маанини туура, анан башкасын өзгөртөт. Албетте, дагы көп тесттерди жазуу керек, бирок тестирлөө биздин учурда негизги тема эмес:
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);
   }

}
Мен азыр тесттер жөнүндө бир нече комментарий бере алам. Алар JUnit 5 аркылуу жазылган:
  1. Тест тиешелүү annotationга ээ болсо, тест катары каралат - @Test.
  2. Эгер ат тесттин ишин сүрөттөп берүү кыйын болсо же сыпаттоо узун жана окууга ыңгайсыз болсо, @DisplayName annotationсын кошуп, аны сыноолорду жүргүзүүдө көрүнүп турган кадимки сүрөттөмө кылып алсаңыз болот.
  3. Тесттерди жазууда мен BDD ыкмасын колдоном, анда мен тесттерди логикалык бөлүктөргө бөлөм:
    1. //берилген - тестке чейин маалыматтарды орнотуу блогу;
    2. //codeдун биз сынап жаткан бөлүгү ишке кирген блок качан;
    3. //анда качан блогунун натыйжалары текшерилүүчү блок.
Эгер сиз аларды иштетсеңиз, алар баары күтүлгөндөй иштеп жатканын ырасташат.

  • stripStart ыкмасы

Бул жерде мен башында боштуктар жана үтүрлөр болушу мүмкүн болгон сызык менен маселени чечүү керек болчу. Трансформациядан кийин алар жаңы мааниге ээ болбошу керек болчу. Проблеманын билдирүүсү мурдагыдан да ачык. Бир нече мисалдар биздин түшүнүгүбүздү бекемдейт: “, , китептер” -> “китептер” “,,, китептер” -> “китептер” b , китептер” -> “b , китептер” leftPad менен болгон сыяктуу, мен StrimStartExample классы , анын эки ыкмасы бар. Бири - өзүнүн чечими менен:
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);
}
Бул жерде эч кандай боштук же үтүр калбаган индексти табуу идеясы болгон. Эгерде алар башында такыр жок болсо, анда индекс нөлгө барабар болот. Ал эми экинчиси - StringUtils аркылуу чечим менен :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Бул жерде биз кайсы сап менен иштеп жатканыбыз тууралуу биринчи аргумент маалыматын өткөрүп беребиз, ал эми экинчисинде өткөрүп жиберүү керек болгон символдордон турган сапты өткөрүп беребиз. Биз StripStartExampleTest классын ушундай жол менен түзөбүз :
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 ыкмасы

Бул ыкма, албетте, алда канча жөнөкөй, бирок бул аны анча пайдалуу кылbyte. Ал String.isEmpty() методунун мүмкүнчүлүктөрүн кеңейтет , ал ошондой эле нөлдү текшерүүнү кошот. Эмне үчүн? NullPointerException, башкача айтканда null болгон өзгөрмө боюнча ыкмаларды чакырбоо үчүн . Ошондуктан, жазбаш үчүн:
if(value != null && value.isEmpty()) {
   //doing something
}
Сиз муну жөн гана кыла аласыз:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Бул ыкманын артыкчылыгы – кайсыл жерде колдонулганы дароо эле көрүнүп турат.

2. StringUtils классынын башка методдорун анализдөө

Эми, менин оюмча, көңүл бурууга татыктуу болгон ыкмалар жөнүндө сүйлөшөлү. Жалпысынан StringUtils жөнүндө сөз кыла турган болсок, ал String классында табылгандарга окшош нөлдүк коопсуз методдорду камсыздайт ( isEmpty методундагыдай ). Келгиле, аларды карап көрөлү:

  • салыштыруу ыкмасы

Мындай ыкма Stringде бар жана эки сапты салыштырганда алардын бири нөл болсо, NullPointerException ыргытат. Кодубуздагы жаман текшерүүлөрдү болтурбоо үчүн, биз StringUtils.compare(String str1, String str2) ыкмасын колдонсок болот : ал салыштыруунун натыйжасында int кайтарат. Бул баалуулуктар эмнени билдирет? int = 0, эгерде алар бирдей болсо (же экөө тең нөл). int < 0, эгерде str1 str2ден аз болсо. int > 0, эгерде str1 str2ден чоң болсо. Ошондой эле, алардын documentтерин карасаңыз, бул ыкманын Javadoc төмөнкү сценарийлерди көрсөтөт:
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

  • ... ыкмаларын камтыйт

Бул жерде коммуналдык иштеп чыгуучулар бир жарылуу болду. Сиз каалаган ыкма бар. Мен аларды бириктирүүнү чечтим:
  1. камтыйт - күтүлгөн саптын башка саптын ичинде экендигин текшерген ыкма. Бул кандайча пайдалуу? Эгер текстте белгилүү бир сөз бар экенине ынануу керек болсо, бул ыкманы колдонсоңуз болот.

    Мисалдар:

    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

    Дагы, NPE (Null Pointer Exception) коопсуздугу бар.

  2. containAny - бул сапта болгон белгилердин бар же жок экенин текшерген ыкма. Ошондой эле пайдалуу нерсе: муну көп учурда жасоого туура келет.

    Документтен мисалдар:

    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 методуна пайдалуу кеңейтүү . Чынында эле, бул ыкма жок, мындай ишти текшерүү үчүн, бир нече жолдор менен өтүүгө туура келет. Ошентип, бир гана ыкма шайкеш колдонулат.

  4. Документтерден бир нече мисал:

    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 - атына карап, эмне текшерorп жатканын түшүнө аласыз. Ичинде сызыктар болбошу керек. Пайдалуу нерсе, албетте. Кээ бир керексиз каармандарды тез издөө;). Биздин телеграмма ботубузда биз уятсыз сөздөрдү чыпкалайбыз жана бул күлкүлүү ыкмаларды этибарга албайбыз.

    Жана мисалдар, аларсыз биз кайда болмокпуз:

    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 ыкмасы

Эгерде сап нөл болсо жана сиз кандайдыр бир демейки маанини коюшуңуз керек болсо, кошумча маалыматты кошуудан качууга жардам берген бир катар ыкмалар. Ар кандай табитке ылайыктуу көптөгөн варианттар бар. Алардын эң негизгиси - StringUtils.defaultString(акыркы Сап стр, акыркы Сап defaultStr) - str нөл болсо, биз жөн гана маанини defaultStrге өткөрүп беребиз . Документтен мисалдар:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Маалыматтар менен POJO классын түзгөндө колдонууга абдан ыңгайлуу.

  • deleteWhitespace ыкмасы

Бул кызыктуу ыкма, бирок аны колдонуунун көптөгөн варианттары жок. Ошол эле учурда, эгерде мындай жагдай жаралса, ыкма сөзсүз түрдө абдан пайдалуу болот. Ал саптан бардык боштуктарды жок кылат. Бул боштук кайда болбосун, анын изи да калbyte))) Документтерден мисалдар:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • endsWith ыкмасы

Өзү үчүн сүйлөйт. Бул абдан пайдалуу ыкма: ал сап сунуш кылынган сап менен аяктаарын текшерет. Бул көп учурда зарыл. Албетте, чекти өзүңүз жазсаңыз болот, бирок даяр ыкманы колдонуу ыңгайлуу жана жакшыраак. Мисалдар:
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
Көрүнүп тургандай, баары бош сап менен бүтөт))) Мен бул мисал (StringUtils.endsWith("ABCDEF", "") = чындык) жөн гана бонус деп ойлойм, анткени бул абсурд) Ошондой эле бир ыкма бар учурга көңүл бурbyte.

  • ыкмасына барабар

Эки сапты салыштырган нөлдүк коопсуз ыкманын сонун үлгүсү. Ал жерге эмнени киргизсек, жооп ошол жерде болот жана катасыз болот. Мисалдар:
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
Албетте, equalsIgnoreCase да бар - бардыгы дал ушундай жол менен жасалат, биз гана ишти этибарга албайбыз. көрөлү?
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

  • ар кандай ыкмага барабар

Келгиле, барабар ыкмасын кеңейтели . Келгиле, бир нече теңчorкти текшерүүнүн ордуна биз бир текшерүүнү аткаргыбыз келет дейли. Бул үчүн, биз саптардын жыйындысы салыштырыла турган сапты өткөрө алабыз; эгерде алардын бири сунушталганга барабар болсо, анда ал ЧЫНДЫК болот. Биз сапты жана саптардын жыйнагын бири-бири менен салыштыруу үчүн өткөрөбүз (жыймадагы саптар менен биринчи сап). Кыйынбы? Бул жерде эмнени билдирерин түшүнүүгө жардам берүү үчүн documentтерден мисалдар келтирилген:
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
Ошондой эле equalsAnyIgnoreCase бар . Жана ага мисалдар:
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

Төмөнкү сызык

Натыйжада, биз StringUtils деген эмне жана анын кандай пайдалуу ыкмалары бар экенин билүү менен кетебиз. Ооба, ушундай пайдалуу нерселер бар экенин түшүнүү менен жана даяр чечимдин жардамы менен маселени жабууга мүмкүн болгон жерлерде балдак менен тосуунун кереги жок. Жалпысынан биз методдордун бир бөлүгүн гана талдап чыктык. Кааласаңыз, мен уланта алам: алар дагы көп жана алар көңүл бурууга татыктуу. Эгер сизде муну кантип көрсөтүүгө боло турган идеяларыңыз болсо, жазыңыз - мен ар дайым жаңы идеяларга ачыкмын. Методдор үчүн documentтер абдан жакшы жазылган, натыйжалары менен тест мисалдары кошулган, бул ыкманын иштешин жакшыраак түшүнүүгө жардам берет. Ошондуктан, биз documentтерди окуудан тартынбайбыз: бул утorтанын иштешине байланыштуу шектенүүлөрүңүздү жок кылат. Жаңы codeдоо тажрыйбасын алуу үчүн, мен сизге пайдалуу класстар кандайча жасалып, жазылганын карап көрүүнү сунуштайм. Бул келечекте пайдалуу болот, анткени адатта ар бир долбоордун өзүнүн сыныктары бар жана аларды жазуу тажрыйбасы пайдалуу болот. Салт боюнча, мен сизге Githubдагы аккаунтума жазылууну сунуштайм ) Менин телеграмма боту менен долбоорум жөнүндө билбегендер үчүн бул жерде биринчи макалага шилтеме . Окуу үчүн баарына рахмат. Мен төмөндө кээ бир пайдалуу шилтемелерди коштум.
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION