JavaRush /Java Blog /Random EN /Let's break down the StringUtils class

Let's break down the StringUtils class

Published in the Random EN group
Hello everyone, my dear readers. I try to write about what really interests me and what worries me at the moment. Therefore, today there will be some light reading that will be useful to you as a reference in the future: let's talk about StringUtils . Let's break down the StringUtils class - 1It just so happened that at one time I bypassed the Apache Commons Lang 3 library . This is a library with auxiliary classes for working with different objects. This is a collection of useful methods for working with strings, collections, and so on. On a current project, where I had to work in more detail with strings in translating 25-year-old business logic (from COBOL to Java), it turned out that I did not have a deep enough knowledge of the StringUtils class . So I had to create everything myself. What I mean? The fact that you don’t have to write certain tasks involving string manipulation yourself, but use a ready-made solution. What's wrong with writing it yourself? At least in that this is more code that has already been written a long time ago. No less pressing is the issue of testing the code that is written additionally. When we use a library that has proven itself to be good, we expect that it has already been tested and that we don't need to write a bunch of test cases to test it. It just so happens that the set of methods for working with a string in Java is not so large. There are really not many methods that would be useful for work. This class is also created to provide checks for NullPointerException. The outline of our article will be as follows:
  1. How to connect?
  2. Examples from my work: how, without knowing about such a useful class, I created my bicycle crutch.
  3. Let's look at other methods that I found interesting.
  4. Let's summarize.
All cases will be added to a separate repository in the Javarush Community organization on GitHub. There will be separate examples and tests for them.

0. How to connect

Those who walk hand in hand with me are already more or less familiar with both Git and Maven, so further I will rely on this knowledge and not repeat myself. For those who missed my previous articles or just started reading, here are materials about Maven and Git . Of course, without a build system (Maven, Gredl), you can also connect everything manually, but this is crazy nowadays and you definitely don’t need to do it like that: it’s better to immediately learn how to do everything correctly. Therefore, to work with Maven, we first add the appropriate dependency:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Where ${apache.common.version} is the version of this library. Next, to import in some class, add import:
import org.apache.commons.lang3.StringUtils;
And that's it, it's all in the bag))

1. Examples from a real project

  • leftPad method

The first example generally seems so stupid now that it’s very good that my colleagues knew about StringUtils.leftPad and told me. What was the task: the code was built in such a way that it was necessary to transform the data if it did not arrive quite correctly. It was expected that the string field should consist only of numbers, i.e. if its length is 3 and its value is 1, then the entry should be “001”. That is, first you need to remove all spaces, and then cover it with zeros. More examples to make the essence of the task clear: from “12“ -> “012” from “1“ -> “001” And so on. What did I do? Described this in the LeftPadExample class . I wrote a method that will do all this:
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();
}
As a basis, I took the idea that we can simply get the difference between the original and the trimmed value and fill it with zeros in front. To do this I used IntStream to do the same operation n times. And this definitely needs to be tested. Here's what I could have done if I had known about the StringUtils.leftPad method in advance :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
As you can see, there is much less code, and a library confirmed by everyone is also used. For this purpose, I created two tests in the LeftPadExampleTest class (usually when they plan to test a class, they create a class with the same name + Test in the same package, only in src/test/java). These tests check one method to ensure that it correctly transforms the value, then another. Of course, a lot more tests would need to be written, but testing is not the main topic in our case:
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);
   }

}
I can make a few comments about the tests for now. They are written using JUnit 5:
  1. A test will be treated as a test if it has the appropriate annotation - @Test.
  2. If it is difficult to describe the operation of the test in the name or the description is long and inconvenient to read, you can add the @DisplayName annotation and make it a normal description that will be visible when running tests.
  3. When writing tests, I use the BDD approach, in which I divide the tests into logical parts:
    1. //given - data setup block before the test;
    2. //when is the block where the part of the code that we are testing is launched;
    3. //then is a block in which the results of the when block are checked.
If you run them, they will confirm that everything is working as expected.

  • stripStart method

Here I needed to solve an issue with a line that could have spaces and commas at the beginning. After the transformation, they should not have had a new meaning. The problem statement is clearer than ever. A few examples will reinforce our understanding: “, , books” -> “books” “,,, books” -> “books” b , books” -> “b , books” As in the case with leftPad, I added the StrimStartExample class , in which has two methods. One - with its own solution:
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);
}
Here the idea was to find the index starting from which there are no more spaces or commas. If they were not there at all at the beginning, then the index will be zero. And the second one - with a solution via StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Here we pass the first argument information about which string we are working with, and in the second we pass a string consisting of characters that need to be skipped. We create the StripStartExampleTest class in the same way :
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 method

This method, of course, is much simpler, but that doesn’t make it any less useful. It extends the capabilities of the String.isEmpty() method , which also adds a check for null. For what? To avoid NullPointerException, that is, to avoid calling methods on a variable that is null . Therefore, in order not to write:
if(value != null && value.isEmpty()) {
   //doing something
}
You can simply do this:
if(StringUtils.isEmpty(value)) {
   //doing something
}
The advantage of this method is that it is immediately clear where which method is used.

2. Analysis of other methods of the StringUtils class

Now let's talk about those methods that, in my opinion, also deserve attention. Speaking generally about StringUtils , it is worth saying that it provides null safe methods analogous to those found in the String class (as is the case with the isEmpty method ). Let's go through them:

  • compare method

Such a method exists in String and will throw a NullPointerException if, when comparing two strings, one of them is null. To avoid ugly checks in our code, we can use the StringUtils.compare(String str1, String str2) method : it returns an int as the result of the comparison. What do these values ​​mean? int = 0 if they are the same (or both are null). int < 0, if str1 is less than str2. int > 0, if str1 is greater than str2. Also, if you look at their documentation, the Javadoc of this method presents the following scenarios:
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

  • contains... methods

Here the utility developers had a blast. Whatever method you want is there. I decided to put them together:
  1. contains is a method that checks whether the expected string is inside another string. How is this useful? You can use this method if you need to make sure that there is a certain word in the text.

    Examples:

    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

    Again, NPE (Null Pointer Exception) security is present.

  2. containsAny is a method that checks whether any of the characters present in the string are present. Also a useful thing: you often have to do this.

    Examples from the documentation:

    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. containsIgnoreCase is a useful extension to the contains method . Indeed, to check such a case without this method, you will have to go through several options. And so only one method will be used harmoniously.

  4. A few examples from the docs:

    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. containsNone - judging by the name, you can already understand what is being checked. There should be no lines inside. A useful thing, definitely. Quick search for some unwanted characters ;). In our telegram bot we will filter obscenities and will not ignore these funny methods.

    And examples, where would we be without them:

    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 method

A series of methods that help avoid adding an extra information if the string is null and you need to set some default value. There are many options to suit every taste. Chief among them is StringUtils.defaultString(final String str, final String defaultStr) - in case str is null, we will simply pass the defaultStr value. Examples from the documentation:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
It is very convenient to use when you create a POJO class with data.

  • deleteWhitespace method

This is an interesting method, although there are not many options for its application. At the same time, if such a case arises, the method will definitely be very useful. It removes all spaces from the string. Wherever this gap is, there will be no trace of it))) Examples from the docs:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • endsWith method

Speaks for itself. This is a very useful method: it checks whether the string ends with the suggested string or not. This is often necessary. Of course, you can write the check yourself, but using a ready-made method is clearly more convenient and better. Examples:
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
As you can see, everything ends with an empty line))) I think that this example (StringUtils.endsWith("ABCDEF", "") = true) is just a bonus, because this is absurd) There is also a method that ignores case .

  • equals method

A great example of a null safe method that compares two strings. Whatever we put in there, the answer will be there, and it will be without errors. Examples:
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
Of course, there is also equalsIgnoreCase - everything is done in exactly the same way, only we ignore the case. Let's see?
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

  • equalsAny method

Let's go ahead and extend the equals method . Let's say that instead of several equality checks, we want to perform one. For this, we can pass a string with which a set of strings will be compared; if any of them is equal to the proposed one, it will be TRUE. We pass a string and a collection of strings to compare them with each other (the first string with the strings from the collection). Difficult? Here are examples from the docs to help you understand what is meant:
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
There is also equalsAnyIgnoreCase . And examples for it:
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

Bottom line

As a result, we leave with the knowledge of what StringUtils is and what useful methods it has. Well, with the realization that there are such useful things and there is no need to fence each time with crutches in places where it would be possible to close the issue with the help of a ready-made solution. In general, we have analyzed only part of the methods. If you wish, I can continue: there are many more of them, and they really deserve attention. If you have any ideas on how else this could be presented, please write - I'm always open to new ideas. The documentation for the methods is written very well, test examples with results are added, which helps to better understand the operation of the method. Therefore, we don’t shy away from reading the documentation: it will dispel your doubts about the functionality of the utility. To gain new coding experience, I advise you to look at how utility classes are made and written. This will be useful in the future, since usually each project has its own scrap classes, and the experience of writing them will come in handy. Traditionally, I suggest you subscribe to my account on Github ) For those who do not know about my project with a telegram bot, here is a link to the first article . Thanks everyone for reading. I've added some useful links below.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION