1. Overview
In this tutorial, we’ll explore a few different ways to get the last n characters from a String.
Let’s pretend that we have the following String whose value represents a date:
String s = "10-03-2024";
From this String, we want to extract the year. In other words, we want just the last four characters, so, n is:
int n = 4;
2. Using the substring() Method
We can use an overload of the substring() method that obtains the characters starting inclusively at the beginIndex and ending exclusively at the endIndex:
@Test
void givenString_whenUsingTwoArgSubstringMethod_thenObtainLastNCharacters() {
int beginIndex = s.length() - n;
String result = s.substring(beginIndex, s.length());
assertThat(result).isEqualTo("2024");
}
Since we want the last n characters, we first determine our beginIndex by subtracting n from the length of the String. Finally, we supply the endIndex as simply the length of the String.
And since we’re only interested in the last n characters, we can make use of the more convenient overload of the substring() method that only takes the beginIndex as a method argument:
@Test
void givenString_whenUsingOneArgSubstringMethod_thenObtainLastNCharacters() {
int beginIndex = s.length() - n;
assertThat(s.substring(beginIndex)).isEqualTo("2024");
}
This method returns the characters ranging from the beginIndex inclusively and terminates at the end of the String.
Finally, it’s worth mentioning the existence of the subSequence() method of the String class, which uses substring() under the hood. Even though it could be used to solve our use case, it would be considered more appropriate to just use the substring() method from a readability perspective.
3. Using the StringUtils.right() Method From Apache Commons Lang 3
To use Apache Commons Lang 3, we need to add its dependency to our pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
We can use the StringUtils.right() method to obtain the last n characters:
@Test
void givenString_whenUsingStringUtilsRight_thenObtainLastNCharacters() {
assertThat(StringUtils.right(s, n)).isEqualTo("2024");
}
Here, we just need to supply the String in question and the number of characters we want to return from the end of the String. Thus, this would be the preferred solution for our use case as we remove the need to calculate indexes, resulting in more maintainable and bug-free code.
4. Using the Stream API
Now, let’s explore what a functional programming solution might look like.
One option for our Stream source would be to use the chars() method. This method returns an IntStream whose primitive int elements represent the characters in the String:
@Test
void givenString_whenUsingIntStreamAsStreamSource_thenObtainLastNCharacters() {
String result = s.chars()
.mapToObj(c -> (char) c)
.skip(s.length() - n)
.map(String::valueOf)
.collect(Collectors.joining());
assertThat(result).isEqualTo("2024");
}
Here, we use the mapToObj() intermediate operation to convert each int element to a Character object. Now that we have a Character stream, we can use the skip() operation to retain only the elements after a certain index.
Next, we use the map() operation with the String::valueOf method reference. We convert each Character element to a String because we want to use the terminal collect() operation with the Collectors.joining() static method.
Another functional approach uses the toCharArray() method initially instead:
@Test
void givenString_whenUsingStreamOfCharactersAsSource_thenObtainLastNCharacters() {
String result = Arrays.stream(ArrayUtils.toObject(s.toCharArray()))
.skip(s.length() - n)
.map(String::valueOf)
.collect(Collectors.joining());
assertThat(result).isEqualTo("2024");
}
This method returns an array of char primitives. We can use the ArrayUtils.toObject() from Apache Commons Lang 3 to convert our array to an array of Character elements. Finally, we obtain a Character stream using the static method Arrays.stream(). From here, our logic remains the same to obtain the last n characters.
As we can see above, we need to do a lot of work to achieve our goal using a functional programming approach. This highlights that functional programming should only be used when it’s appropriate to do so.
5. Conclusion
In this article, we’ve explored several different ways to get the last n characters from a String. We highlighted that an imperative approach over a functional one yielded a more concise and readable solution. We should note that the examples of extracting the year from a String explored in this article were just for demonstration purposes. More appropriate ways of achieving this exist such as parsing the String as a LocalDate and using the getYear() method.
As always, the code samples used in this article are available over on GitHub.