The Answers by dan1st and by WJS are both correct and smart. I'll modify the code seen in the first to take advantage of new features in Java.
tl;dr
Use the new SequencedCollection
interface in Java 21+.
public SequencedCollection < Integer > calculateStringLengths ( final List < String > inputList )
{
return inputList
.stream ( )
.map ( ( String s ) -> Math.toIntExact ( s.codePoints ( ).count ( ) ) ) // Calculate the true count of characters in this string.
.toList ( );
}
Sequenced collections in Java 21+
Java 21 brings some new interfaces and methods to the venerable Java Collections framework. The new features recognize the concept of sequenced collections.

For more info, see JEP 431: Sequenced Collections. And see the video by José Paumard on the JEP Café channel on YouTube. See early-access Javadoc.
Let's modify the code shown in the Answer by dan1st to use the new Java 21 features.
Start with this piece:
public static Set<Integer> test(List<String> inputList){
return inputList.stream()
.map(String::length)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
Notice that this method must return either:
- The overly general
Set
interface, with no indication of this collection promising to be in a certain order.
- The overly specific
LinkedHashSet
class, which remembers insertion order.
Java offers the SortedSet
interface, and its successor NavigableSet
. These are meant for collections with a sorted order. But LinkedHashSet
with its insertion order cannot implement those interfaces.
So, voilà, Java 21 adds the SequencedSet
interface, to represent LinkedHashSet
as well as SortedSet
/NavigableSet
implementations such as TreeSet
.
Let's pop that into the code snippet from above. And do some reformatting and renaming.
public SequencedSet < Integer > calculateStringLengths ( List < String > inputList )
{
return inputList
.stream ( )
.map ( String :: length )
.collect (
Collectors.toCollection ( LinkedHashSet :: new )
);
}
And we could make that the more general SequencedCollection
.
public SequencedCollection < Integer > calculateStringLengths ( List < String > inputList )
{
return inputList
.stream ( )
.map ( String :: length )
.collect (
Collectors.toCollection ( LinkedHashSet :: new )
);
}
Le's try it.
List < String > listOfStrings = List.of ( "aaaa" , "aa" , "aaa" , "a" );
SequencedCollection < Integer > stringLengths = this.calculateStringLengths ( listOfStrings );
System.out.println ( "stringLengths = " + stringLengths );
When run:
stringLengths = [4, 2, 3, 1]
Set
forbids duplicates
But multiple input strings could very well have the same length. And Set
implementations cannot allow duplicates. So really, we should be outputting a List
rather than a Set
.
Fortunately, no need to change the signature of our calculateStringLengths
method — List
& ArrayList
implement SequencedCollection
. So we can swap:
.collect (
Collectors.toCollection ( LinkedHashSet :: new )
);
… with:
.toList() ;
… like this:
public SequencedCollection < Integer > calculateStringLengths ( List < String > inputList )
{
return inputList
.stream ( )
.map ( String :: length )
.toList() ;
}
We get the same results.
stringLengths = [4, 2, 3, 1]
Use code points, not char
Unfortunately, you used the String#length
method which is essentially broken. Add a character such as
to your input strings to see incorrect results.
That length
method uses the char
type. As a 16-bit value, that char
type is physically incapable of representing most characters. Instead of char
use code points.
Replace String :: length
with s.codePoints ( ).count ( )
. But that call returns a long
, so we must either change < Integer >
to < Long >
or we must cast the long
to an int
since a string’s length cannot be more than Integer.MAX_VALUE
. For defensive programming, we can be extra safe by using Math.toIntExact
instead of a direct cast, as this method throws an exception in case of integer overflow.
public SequencedCollection < Integer > calculateStringLengths ( final List < String > inputList )
{
return inputList
.stream ( )
.map ( ( String s ) -> Math.toIntExact ( s.codePoints ( ).count ( ) ) ) // Calculate the true count of characters in this string.
.toList ( );
}
Let's try this final version of our code. We add
as a test of our more accurate string-length calculation.
List < String > listOfStrings = List.of ( "aaaa" , "aa" , "aaa" , "a" , "" );
SequencedCollection < Integer > stringLengths = this.calculateStringLengths ( listOfStrings );
System.out.println ( "stringLengths = " + stringLengths );
stringLengths = [4, 2, 3, 1, 1]