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