- How to connect?
- Examples from my work: how, without knowing about such a useful class, I created my own
bicyclecrutch. - We analyze other methods that seemed interesting to me.
- Let's summarize.
0. How to connect
Those who go toe to toe 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 joined the reading, here are the materials about maven and gita . Of course, without a build system (maven, gredl) you can also connect everything manually, but this is wild in our time and you definitely don’t need to do this: it’s better to learn how to do everything right right away. Therefore, to work with maven, 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 an import:
import org.apache.commons.lang3.StringUtils;
And that's it, it's in the hat))
1. Examples from a real project
- leftPad method
The first example generally seems so stupid now that it's good that my colleagues knew about StringUtils.leftPad and prompted me. What was the task: the code was built in such a way that it was necessary to transform the data if they did not come 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 pave it with zeros. More examples to make the essence of the problem 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 does it all:
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();
}
I took as a basis the idea that we can simply get the difference between the original and the trimmed value and fill in front with zeros. For this, I used an IntStream to do the same operation n times. And it definitely needs to be tested. And here is what could have been done if I knew in advance about the StringUtils.leftPad method :
public static String apacheCommonLeftPad(String value) {
return StringUtils.leftPad(value.trim(), value.length(), "0");
}
As you can see, there is much less code, while still using a well-established library. For this case, 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 first one method to correctly transform the value, then another. Of course, much more tests would need to be written, but the topic of testing in our case is not the main one:
package com.github.codegymcommunity.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 according to JUnit 5:
- A test will be treated as a test if it has the corresponding @Test annotation.
- If it is difficult to describe the work 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 the tests are run.
- When writing tests, I use the BDD approach, in which I divide tests into logical parts:
- //given - data setup block before the test;
- //when - the block where the part of the code that we are testing is launched;
- //then is a block in which the results of the when block work are checked.
- stripStart method
Here I needed to solve the issue with a string that could have spaces and commas at the beginning. After the transformation, they should not have been in the new meaning. The problem statement is as clear as ever. A few examples will solidify our understanding: “, , books” -> “books” “,,,books” -> “books” b , books” -> “b , books ” 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 and commas. If they were not at all at the beginning, then the index will be zero. And the second - with a solution through StringUtils :
public static String apacheCommonLeftPad(String value) {
return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Here we pass in the first argument information about which string we are working with, and in the second we pass a string consisting of characters to be skipped. In the same way, we create the StripStartExampleTest class:
package com.github.codegymcommunity.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 it is no less useful for that. It extends the String.isEmpty() method by adding a null check. For what? To avoid NullPointerException, that is, to avoid calling methods on a variable that is null . So instead of writing:
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 you can immediately see where which method is used.
2. Parsing 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's worth saying that it provides null-safe methods similar to those found in the String class (as is the case with the isEmpty method ). Let's go through them:
- compare method
There is such a method in String and there will be a NullPointerException if one of them is null in comparing two strings. 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, then the following scenarios are presented in the Javadoc of this method:
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 developers of the utility went wild. Whatever method you want. I decided to put them together:
-
contains is a method that checks if the intended string is inside another string. Why is it 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.
containsAny - a method that checks if there is at least some character from those presented in the string. Also a useful thing: often you have to do this. Examples from 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
-
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 harmoniously used.
-
containsNone - judging by the name, you can already understand what is being checked. There should be no lines inside. Useful stuff, definitely. Quick search for some objectionable characters ;). In our telegram bot we will filter mats, we will not pass by these funny methods.
And examples, where 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
Some 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
- defaultString method
A series of methods that help avoid adding an extra if if the string is null and some default value needs to be set. There are many options for every taste. Chief among them is StringUtils.defaultString(final String str, final String defaultStr) - in case str is null, we'll just pass the defaultStr value. Examples from documentation:
StringUtils.defaultString(null, "NULL") = "NULL"
StringUtils.defaultString("", "NULL") = ""
StringUtils.defaultString("bat", "NULL") = "bat"
It is very convenient to use it when you create a POJO class with data.
- deleteWhitespace method
This is an interesting method, although there are not so many options for its application. However, if such an opportunity presents itself, the method will definitely be very useful. It removes all spaces from a string. Wherever this gap is, there will be no trace of it))) Examples from the docks:
StringUtils.deleteWhitespace(null) = null
StringUtils.deleteWhitespace("") = ""
StringUtils.deleteWhitespace("abc") = "abc"
StringUtils.deleteWhitespace(" ab c ") = "abc"
- endsWith method
Speaks for himself. This is a very useful method: it checks if a string ends with the suggested string or not. Often this is needed. Of course, you can write a 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 string))) I think that this example (StringUtils.endsWith("ABCDEF", "") = true) just comes as 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 there, the answer will be, and 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 exactly the same, only we ignore 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
Go ahead and extend the equals method . Let's say instead of multiple equality tests, we want to perform one. For this, we can pass a string with which they will also compare a set of strings, if any of them is equal to the proposed one, then 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 I mean:
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
GO TO FULL VERSION