I've got:
String s = "ZpglnRxqenU"
I need to assign a number to each character in the string like:
z-1
p-2
g-3
l-4
n-5
r-6
x-7
q-8
e-9
n-10
u-11
I do not want to count the frequency of characters.
I tried to use HashMap:
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
map.put(String.valueOf(s.charAt(i)), i + 1);
}
But Map a has unique key and I lost first n occurrence
How do I count letters?
If you want to count the number of characters in a string use s.length();.
If you want to count the number of different characters in a String, you already can with the code you wrote. map.size() will give exactly that, because the map only stores each key once (in your case the 'letters' (they are called char's in java, chars is a diminutive for characters)).
How put() in maps work:
The first time you put a key to the map, it is added with the value you give, the second time the value is changed.
Swap the key-value of your map. Use the number for the key, as it will be unique.
Use a NavigableMap to keep them in order.
NavigableMap< Integer , String > = new TreeMap<>() ;
Example code using conventional style.
String input = "ZpglnRxqenU";
int[] codePoints = input.codePoints().toArray();
NavigableMap < Integer, String > numberedCharacters = new TreeMap <>();
for ( int ordinal = 1 ; ordinal <= codePoints.length ; ordinal++ )
{
numberedCharacters.putIfAbsent(
ordinal ,
Character.toString( codePoints[ ordinal - 1 ] )
);
}
Example code using streams & lambdas. Same effect, not necessarily better in this particular case.
String input = "ZpglnRxqenU";
int[] codePoints = input.codePoints().toArray();
NavigableMap < Integer, String > numberedCharacters =
IntStream
.rangeClosed( 1 , codePoints.length )
.boxed()
.collect(
Collectors.toMap(
Function.identity() ,
ordinal -> Character.toString( codePoints[ ordinal - 1 ] ) ,
( o1 , o2 ) -> o1 ,
TreeMap :: new )
);
To get all the characters from the map, call values. The resulting Collection object promises to iterate in the order of iteration of the map’s keys.
String recreated = String.join( "" , numberedCharacters.values() );
Dump to console.
System.out.println( "input = " + input );
System.out.println( "numberedCharacters = " + numberedCharacters );
System.out.println( "recreated = " + recreated );
When run.
input = ZpglnRxqenU
numberedCharacters = {1=Z, 2=p, 3=g, 4=l, 5=n, 6=R, 7=x, 8=q, 9=e, 10=n, 11=U}
recreated = ZpglnRxqenU
Basil's solution should be good enough for what you want but if you strictly want to model like in your example you can have a List<Pair<Character, Integer>> as a storage for your data. There are many libraries offering Pair or Tuplestructures or you could just create your own.
Populating your list is a matter of personal taste but in essence you iterate over the characters array, map each character and its index to a new Pair(char, idx) then collect them in a list
Related
I am trying to compare elements in Array. for example,
labels = ["abc1","abc2","abc3","abc4"]
I want to take the String with the highest values. In this case its abc4. I'm pretty new to coding so if anyone could help me with the logic, it would be great.
What you are looking for is a method that compares each string to each other.
Java has a built-in method way to do this for the String class, called the compareTo method. As it's name suggests, it compares one string to another.
String a = "abc1";
String b = "abc2";
String c = "abc1";
System.out.println(a.compareTo(b)); // prints a negative number because `a` is smaller than `b`
System.out.println(b.compareTo(a)); // prints a positive number because `b` is bigger than `a`
System.out.println(c.compareTo(a)); // prints 0 because `a` and `b` have the same letters.
See the official java doc for the compareTo method:
[Returns]the value 0 if the argument string is equal to this string; a value less than 0 if this string is lexicographically less than the string argument; and a value greater than 0 if this string is lexicographically greater than the string argument.
The way you could use this in your example would be:
String biggest = labels[0];
for(int i = 1; i < labels.length; i++){
if(biggest.compareTo(labels[i]) < 0) biggest = labels[i];
}
System.out.println(biggest);
Note: For more details on how this method chooses which one is "bigger", see the java doc (linked above). If you have your own rules about which one should be bigger, then you can make your own method to define that.
UPDATE:
For example, see XtremeBaumer's comment
"abc20".compareTo("abc100") = 1. Indicating that abc20 is bigger than abc100, thus making compareTo() not necessarily useful for the task
Your question need improvement, based on what you said, lets remove all the abc from the Strings ,get the max integer and then return or print "abc" concatenated to the max number :
import java.util.Arrays;
import java.util.stream.IntStream;
public class Solution {
public static void main(String[] args) throws Throwable {
int numberOfElements = 100;
String[] labels = new String[numberOfElements];
Arrays.setAll(labels, element -> "abc" + element);
int max = Arrays.stream(labels).mapToInt(element -> Integer.parseInt(element.substring(3))).max().getAsInt();
System.out.println(String.join(" | ", labels));
System.out.println();
System.out.println();
System.out.println("The max here is : ");
System.out.println("abc" + max);
}
}
Output here :
abc0 | | abc1 | | abc2 | | abc3 | | abc4 ...... || abc99
The max here is :
abc99
Try something like this:
var strings = new ArrayList<String>();
strings.add("abc1");
strings.add("abc2");
strings.add("abc3");
strings.add("abc4");
var integers = strings
.stream()
.map(string -> Integer.valueOf(string.replaceAll("[^0-9]+", "")))
.collect(Collectors.toList());
var max = Collections.max(integers);
var indexMax = integers.indexOf(max);
var maxString = strings.get(indexMax);
System.out.println(maxString);
Simpler way : #mcieciel has already posted this one.
List<String> list = Arrays.asList("abc1", "abc22", "abc33", "abc19");
List<Integer> intList = list.stream()
.map(s -> Integer.parseInt(s.replaceAll("[^0-9]+", "")))
.collect(Collectors.toList());
int index = intList.indexOf(Collections.max(intList));
System.out.println(list.get(index));
Another way is creating a map which will have string and its corresponding integer value in key-value pairs.Then find the max value with its key from the map.
This might be an overkill .
String key = list.stream()
.collect(Collectors.toMap( // creating map like this format : abc1->1 , abc22->22 ...
Function.identity(), // key of the map i.e the string value
s -> Integer.parseInt(s.replaceAll("[^0-9]+", "")), // integer value
(e1, e2) -> e1)) // if multiple same entry exists choose one
.entrySet() // now we have map, we can iterate and find out the key which holds max value
.stream()
.max((e1, e2) -> Integer.compare(e1.getValue(), e2.getValue())) // comparing values
.get()
.getKey();
System.out.println(key);
Note : if you have string values without any digit,both will not work.
I've got a task where I'm to discover how many times 'A', 'C', 'G' and 'T' occur in a string, and return it in the following format:
A:count C:count G:count T:count
I'm very new to Java having started learning Java 3 days ago I've come across literature referring to a HashMap being the most viable and efficient means of storing and retrieving this data - As such, I opted in this method.
I've managed to create the conditionals and store the data, however I'm struggling with the presentation of data as mentioned above.
Apologise for some offensive code in advance, what I have so far is:
public class DNA {
static void characterCount(String dna) {
HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
char[] dnaArray = dna.toCharArray();
charCountMap.put('C', 0);
charCountMap.put('A', 0);
charCountMap.put('G', 0);
charCountMap.put('T', 0);
for (char q : dnaArray) {
if (q == 'A' || q == 'C' || q == 'G' || q == 'T') {
charCountMap.put(q, charCountMap.get(q) + 1);
} else {
continue;
}
}
System.out.println(charCountMap);
}
public static void main(String[] args) {
characterCount("ACTGSRSSDSGGGHHTYTCCCFDT");
}
}
I would appreciate any input, advice or signposting to relevant resources for further learning.
Thank you very much for your time!
One way to get the required output is by doing this:
String output = charCountMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining(" "));
System.out.println(output);
You just need to add the below code for that formatting instead of System.out.println(charCountMap);:
for (Entry<Character, Integer> c : charCountMap.entrySet()) {
System.out.print(c.getKey()+ ":" + c.getValue() + " ");
}
So you will get output like this
A:1 C:4 T:4 G:4
Use "LinkedHashMap" instead of "HashMap" if you want to follow the character sequence.
tl;dr
Generate output using String.format. The %d is a placeholder for a long primitive value produced from our passed Long object via auto-boxing.
String.format(
"C:%d A:%d G:%d T:%d" ,
map.get( "C" ) , map.get( "A" ) , map.get( "G" ) , map.get( "T" )
)
Details
Streams makes easier work of this.
Here is a modified version of code from this article.
The split method returns an array of String objects.
We convert those to uppercase. You could omit this step if you know your inputs to already be in uppercase.
We then filter out any strings that are not our desired CAGT.
Lastly, we use a Collector to get a count of each letter, and store the result in a new map.
String input = "ACTGSRSSDSGGGHHTYTCCCFDT" ;
Map < String , Long > map =
Arrays
.stream(
input.split("")
)
.map( String :: toUpperCase )
.filter( s -> "CAGT".contains( s ) )
.collect(
Collectors.groupingBy(
s -> s , TreeMap :: new , Collectors.counting()
)
)
;
Generate output.
String output =String.format( "C:%d A:%d G:%d T:%d" , map.get( "C" ) , map.get( "A" ) , map.get( "G" ) , map.get( "T" ) ) ;
System.out.println( output ) ;
See this code run live at Ideone.com.
C:4 A:1 G:4 T:4
A possible solution is use an int array and map the character A-T to an index. The array size is the number of characters from A to T. The index 0 maps to A and the index 19 maps to T.
static void characterCount(String dna) {
int[] a = new int[20];
for(char c: dna.toCharArray()){
int i = c-'A';
if (i <= 19) a[i]++;
}
System.out.printf("A:%d C:%d G:%d T:%d\n", a[0],a[2],a[6],a[19]);
}
You may concide upper and lower case charachter as well and decide wether "a" is same or different to "A".
If the same, you can change your code to
char[] dnaArray = dna.toUpperCase().toCharArray();
You might also want to make the logic more stable and check, whether the input string is valid
HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
if (dna == null || dna.isEmpty()) {
return;
}
char[] dnaArray = dna.toUpperCase().toCharArray();
Beside this, you could also start cuonting the hole alphabet and use the int value of the corresponding char.
HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
if (dna == null || dna.isEmpty()) {
return;
}
char[] dnaArray = dna.toUpperCase().toCharArray();
for (char q : dnaArray) {
for (int charachter = 'A'; charachter <= 'Z'; ++charachter) {
if (q == charachter) {
charCountMap.put(q, charCountMap.getOrDefault(q, 0) + 1);
break;
}
}
}
System.out.println(charCountMap);
Output:
{A=1, R=1, C=4, S=4, T=4, D=2, F=1, G=4, H=2, Y=1}
EDIT
Use a TreeMap to sort the map entries
TreeMap<Character, Integer> charCountMap = new TreeMap<Character, Integer>();
Output:
{A=1, C=4, D=2, F=1, G=4, H=2, R=1, S=4, T=4, Y=1}
I have a csv file with characters from SW and would like to find the heaviest character using java stream. Here's a sample of the file:
name;height;mass;hair_color;skin_color;eye_color;birth_year;gender
Luke Skywalker;172;77;blond;fair;blue;19BBY;male
C-3PO;167;75;n/a;gold;yellow;112BBY;n/a
R2-D2;96;32;n/a;white, blue;red;33BBY;n/a
Darth Vader;202;136;none;white;yellow;41.9BBY;male
Leia Organa;150;49;brown;light;brown;19BBY;female
Owen Lars;178;120;brown, grey;light;blue;52BBY;male
Beru Whitesun lars;165;75;brown;light;blue;47BBY;female
Grievous;216;159;none;brown, white;green, yellow;unknown;male
Finn;unknown;unknown;black;dark;dark;unknown;male
Rey;unknown;unknown;brown;light;hazel;unknown;female
Poe Dameron;unknown;unknown;brown;light;brown;unknown;male
Expected output is String "Grievous".
Initially I thought of creating a Character class, where I could store the data and work with objects instead of String array after splitting the line. However, each value can be unknown or n/a, so not too sure how to work around it. Is there a way to achieve this using stream only?
This is my initial attempt, mapping each line to new Person object with fields name and height, however this approach does not handle unknown input properly.
public static String getHeaviestCharacter(String file) throws IOException {
return Files.lines(Paths.get(file))
.map(line -> line.split(";"))
.map(part -> new Person(part[0], part[2]))
.max((p1, p2) -> Integer.compare(p1.getWeight(), p2.getWeight()))
.map(p1.getName());
}
Streams
As others noted, I doubt streams is the best approach to your particular problem. But since you asked, just for fun, I gave it a try. After much web-searching, and much trial-and-error, I seem to have found a solution using streams.
We use NIO.2 classes Path & Files to open the data file.
We define a stream by calling Files.lines.
We omit the header row by calling Stream#skip.
Some of your input rows have non-numeric value "unknown" in our target third field. So we call Stream#filter to ignore those lines. We extract the third field by using String#split while passing the annoying zero-based index number 2.
To get the highest number in our third column, we need to sort. To sort, we extract the third field in a Comparator created via Comparator.comparingInt. To get the needed int value, we parse the text of the third field using Integer.parseInt.
After sorting, we need to access the last element in the stream, as that should have our character with the greatest weight. This seems clumsy to me, but apparently the way to get the last element of a stream is .reduce( ( first , second ) -> second ).orElse( null ). I sure wish we had a Stream#last method!
That last element is a String object, a line of text from your input file. So we need to yet again split the string. But this time when we split, we take the first element rather than the third, as our goal is to report the character’s name. The first element is identified by the annoying zero-based index number of 0.
Voilà, we get Grievous as our final result.
Path path = Paths.get( "/Users/basil_dot_work/inputs.csv" );
if ( Files.notExists( path ) ) { throw new IllegalStateException( "Failed to find file at path: " + path ); }
Stream < String > lines;
try { lines = Files.lines( path , StandardCharsets.UTF_8 ); } catch ( IOException e ) { throw new RuntimeException( e ); }
String result =
lines
.skip( 1L ) // Skip the header row, with column names.
.filter( // Filter out lines whose targeted value is "unknown". We need text made up only of digits.
line -> ! line.split( ";" )[ 2 ].equalsIgnoreCase( "unknown" )
)
.sorted( // Sort by extracting third field’s text, then parse to get an `int` value.
Comparator.comparingInt( ( String line ) -> Integer.parseInt( line.split( ";" )[ 2 ] ) )
)
.reduce( ( first , second ) -> second ).orElse( null ) // Get last element.
.split( ";" )[ 0 ]; // Extract name of character from first field of our one and only line of input left remaining after processing.
System.out.println( "result = " + result );
result = Grievous
Be sure to compare my approach here with that of the other Answer, by Florian Hartung. The other may well be better; I've not yet studied carefully.
Without streams
For comparison, here is more conventional code, with little or no use of streams.
We read lines from the file in the same manner as seen above.
We need to skip the first row, the header row of column titles. But the List object returned by Files.lines is unmodifiable. So we cannot simply delete the first element of that list. So we effectively skip the first line by calling lines.subList( 1 , lines.size() ). The subList command returns a list that is mapped as a view back onto the original, not actually creating a new and separate list. This is efficient and appropriate for our use here.
We define a class as a record to hold each person's details. We use Integer rather than int so that we can hold a null for the lines that carry unknown text rather than a number.
For each line, we directly transfer the textual items to String member fields. But for height and mass we use a ternary operator to either return null or to instantiate a Integer object.
We collect our Person objects by adding to a list.
To get the maximum the Person object whose mass is the largest, we need to ignore those with a null. So we use a simple stream here to make new list of Person objects with non-null mass. This stream could be replaced with a conventional loop, but would be more verbose.
With our filtered list, we call Collections.max while passing a Comparator object that compares the mass member field.
We end up with a single Person object. So we interrogate for its name member field.
Voilà, we get Grievous as our final result.
Path path = Paths.get( "/Users/basil_dot_work/inputs.csv" );
if ( Files.notExists( path ) ) { throw new IllegalStateException( "Failed to find file at path: " + path ); }
List < String > lines;
try { lines = Files.lines( path , StandardCharsets.UTF_8 ).toList(); } catch ( IOException e ) { throw new RuntimeException( e ); }
lines = lines.subList( 1 , lines.size() ); // Skip over first line.
record Person( String name , Integer height , Integer mass , String hair_color , String skin_color , String eye_color , String birth_year , String gender ) { }
List < Person > persons = new ArrayList <>();
for ( String line : lines )
{
String[] parts = line.split( ";" );
Integer height = ( parts[ 1 ].equalsIgnoreCase( "unknown" ) ) ? null : Integer.valueOf( parts[ 1 ] );
Integer mass = ( parts[ 2 ].equalsIgnoreCase( "unknown" ) ) ? null : Integer.valueOf( parts[ 2 ] );
Person person = new Person( parts[ 0 ] , height , mass , parts[ 3 ] , parts[ 4 ] , parts[ 5 ] , parts[ 6 ] , parts[ 7 ] );
persons.add( person );
}
System.out.println( "persons = " + persons );
List < Person > personsWithMass = persons.stream().filter( person -> Objects.nonNull( person.mass ) ).toList();
Person heaviestPerson = Collections.max( personsWithMass , Comparator.comparing( person -> person.mass ) );
System.out.println( "heaviest Person’s name = " + heaviestPerson.name );
heaviest Person’s name = Grievous
I would not recommend doing this with Streams, but instead with some CSV library, as it is way more safe.
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader(new File("characters.csv")));
// Skip first line
reader.readLine();
Optional<String> optionalHeaviestCharacter = getHeaviestCharactersName(reader.lines());
System.out.println(optionalHeaviestCharacter);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Optional<String> getHeaviestCharactersName(Stream<String> lineStream) {
return lineStream
.map(lineString -> lineString.split(";")) // map every line string to an array with all values
.filter(values -> values[2].matches("[0-9]+")) // filter out characters with a non-number value as a mass
.max((values1, values2) -> Integer.compare(Integer.parseInt(values1[2]), Integer.parseInt(values2[2]))) // get element with maximum mass
.map(heaviestValues -> heaviestValues[0]); // map values array of heaviest character to its name
}
First we read the file, which I have names characters.csv. You will probably need to edit the filepath to point to your file.
BufferedReader reader = new BufferedReader(new FileReader(new File("characters.csv")));
Then we read all lines from the file, each line as a String in the Stream<String>, by calling the reader.lines() method
The function getHeaviestCharactersName will then return an Optional<String>. The Optional will be empty, when for example all characters have an unknown/invalid mass or when there are no characters present at all.
If you think that there will always be at least one character with a valid mass present, you get just get the name of the heaviest character with optionalHeaviestCharacter.get().
Else you would have to check if the Optional is empty first:
if (optionalHeaviestCharacter.isEmpty()) {
System.out.println("Could not find a character with the heaviest mass");
} else {
System.out.println("Heaviest character is " + optionalHeaviestCharacter.get());
}
You can just get the name by calling
I'm attempting a block of code that I can't get quite right. I have a list with say, 1000 string entries (of 3 - 10 characters each) that I collect into a single comma-delimited string. If the total size of the characters in the resulting string is more than 8100, I need to split the list and create TWO comma-delimited strings (or 3, or whatever factor of 8100). I know this needs a groupingBy, but my syntax isn't working.
So my first question is, how can I determine the sum of the characters in my list, and how can I group the list such that there are no more than 8100 characters in each group of lists? This is what I have so far:
AtomicInteger counter = new AtomicInteger();
String codes = configInfos.stream()
.map(ConfigInfo::getCode)
.collect(Collectors.groupingBy(it -> counter.getAndIncrement() / 8100))
This needs to be solved with Streams?
Otherwise I would solve it like this:
String [] array = configInfos
.stream()
.map( ConfigInfo::getCode )
.toArray( String []::new );
List<String> codes = new ArrayList<>();
StringJoiner joiner = new StringJoiner( "," );
for( String s : array )
{
if( joiner.length() + 1 + s.length() > 8100 )
{
codes.add( joiner.toString() );
joiner = new StringJoiner( "," );
}
joiner.add( s );
}
if( joiner.length() > 0 ) codes.add( joiner.toString() );
But I have to confess, I have no clue how to solve this with Streams …
Edit: don’t use streams for this
I need the string maintained when I do the split. It has to look like
the original list, with list 1 being "Entries", "are". List 2 would be
"three", "upto". List 3 would be "ten", "chars", and so on.
Stream operations are not suited for your task. I recommend you use a classical loop for the clearest and easiest to maintain code.
Original answer: Intstream.range()
Not sure this is really what you want. In case you prefer to use a stream, here’s my attempt at that.
final int maxSubstringLength = 9; // 8100
List<String> entries = List.of("Entries", "are", "three", "upto", "ten", "chars", "each");
String totalString = entries.stream().collect(Collectors.joining(","));
// round up in the division
int substringCount = (totalString.length() + maxSubstringLength - 1) / maxSubstringLength;
List<String> substrings = IntStream.range(0, substringCount)
.mapToObj(i -> totalString.substring(i * maxSubstringLength, Math.min((i + 1) * maxSubstringLength, totalString.length())))
.collect(Collectors.toList());
substrings.forEach(System.out::println);
Output:
Entries,a
re,three,
upto,ten,
chars,eac
h
For your very long string just put 8100 as max substring length where I put 9 for the demonstration.
My Question is inspired by this Question, but is aimed at using Java Streams to arrive an a List<Integer>.
I have this code that kind of works. It seems to be returning an ArrayList, presumably ArrayList<Integer>. But the compiler refuses to let me declare the result as such.
String input = "1 2 3 4 5";
Stream stream = Arrays.stream( input.split( " " ) );
var x = stream.map( s -> Integer.valueOf( ( String ) s ) ).collect( Collectors.toList() );
This runs when using the new var feature of recent Java versions.
System.out.println( x.getClass() );
System.out.println( x );
class java.util.ArrayList
[1, 2, 3, 4, 5]
I have two questions:
Why is x reported as an ArrayList yet I cannot declare x to be an ArrayList (error: incompatible types), such as:ArrayList<Integer> x = stream.map( s -> Integer.valueOf( ( String ) s ) ).collect( Collectors.toList() );
Is there a better way to use streams to convert this string of digits to a List of Integer?
First, your Stream is raw. Using a raw type means that anything that uses a type parameter is erased to its upper bound, here Object. So map returns another raw Stream and collect returns an Object. Easy fix: Stream<String>.
Stream<String> stream = Arrays.stream( input.split( " " ) );
Second, Collectors.toList is specified to return List<T>, or List<String> here.
There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned; if more control over the returned List is required, use toCollection(Supplier).
If you aren't satisfied with List and you absolutely need an ArrayList, supply one:
.collect(Collectors.toCollection(ArrayList::new));
Incidentally, you can replace the lambda expression
s -> Integer.valueOf( ( String ) s )
with the method reference
Integer::valueOf
After these changes, your code might look like this:
String input = "1 2 3 4 5";
Stream< String > stream = Arrays.stream( input.split( " " ) );
List< Integer > x = stream.map( Integer::valueOf ).collect( Collectors.toList() );
Or, if you insist on precisely ArrayList rather than List, make that:
String input = "1 2 3 4 5";
Stream< String > stream = Arrays.stream( input.split( " " ) );
ArrayList< Integer > x = stream.map( Integer::valueOf ).collect( Collectors.toCollection( ArrayList::new ) );
Once these changes are made, this seems like a reasonably good way of converting a string containing space-separated integers to an ArrayList<Integer>. A minor improvement would be to change the split regular expression argument to "\\s+", to represent one or more whitespace characters. In case "1 2" arrives, with multiple spaces between numbers, this will prevent empty strings that would match in between space characters.
You can create an ArrayList, but you shouldn't:
ArrayList<Integer> x =
stream.map(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new));
Refactoring:
List<Integer> x =
Arrays.stream(input.split(" "))
.map(Integer::valueOf)
.collect(Collectors.toList());
Or, with the Pattern.splitAsStream:
List<Integer> x =
Pattern.compile(" ").splitAsStream("1 2 3 4 5")
.map(Integer::valueOf)
.collect(Collectors.toList());
List<Integer> list = Arrays.stream("1 2 3 4 5".split(" ")).map(s -> Integer.valueOf(s)).collect(Collectors.toList());
This compiles. According to the collectors source code: "There are no guarantees on the type, mutability,
* serializability, or thread-safety of the {#code List} returned;"
Why is x reported as an ArrayList yet I cannot declare x to be an ArrayList.
Because
collect(...) is returning a statically inferred type, based on the type of the result of Collectors.toList().
That is (in this case) Collector<String,?,List<String>> ... according to the Collectors javadoc.
The fact that the actual object is an ArrayList is an implementation detail that could conceivably change in future versions of Java.
Is there a better way to use streams to convert this string of digits to a List of Integer?
I will leave that to others to say. However, I think that a simpler / cleaner way to do this is to not use streams for this task.
String input = "1 2 3 4 5";
var x = new ArrayList<>(Arrays.asList(input.split(" ")));
The above code is simpler and most likely more efficient than the streams-based versions I have seen.