Java Stream API collect method

Viewed 646

There is class Person:

class Person {
    private String id;
    private String name;
    private int age;
    private int amount;
}

and I create HashMap of Persons using external file contains lines:

001,aaa,23,1200
002,bbb,24,1300
003,ccc,25,1400
004,ddd,26,1500

Mainclass.java

public class Mainclass {
public static void main(String[] args) throws IOException {
    List<Person> al = new ArrayList<>();
    Map<String,Person> hm = new HashMap<>();
    try (BufferedReader br = new BufferedReader(new FileReader("./person.txt"))) {
        hm = br.lines().map(s -> s.split(","))
                .collect(Collectors.toMap(a -> a[0], a-> new Person(a[0],a[1],Integer.valueOf(a[2]),Integer.valueOf(a[3]))));
    }
}

}

It works fine for HashMap.

How to do the same for ArraList?

I tried:

    al = br.lines().map(s -> s.split(","))
                    .collect(Collectors.toList(a -> new Person(a[0],a[1],Integer.valueOf(a[2]),Integer.valueOf(a[3]))));

(IntelijIdea is underlined in red "a[0]" and says "Array type excpected,found : lambda parameter")

5 Answers

You should use map in order to map each array to a corresponding Person instance:

al = br.lines().map(s -> s.split(","))
               .map (a -> new Person(a[0],a[1],Integer.valueOf(a[2]),Integer.valueOf(a[3])))
               .collect(Collectors.toList());

BTW, Collectors.toList() returns a List, not an ArrayList (even if the default implementation does return ArrayList, you can't count on that).

You need to map it to the Person object before trying to collect it:

.map(s -> s.split(","))
.map(a -> new Person(a[0],a[1],Integer.valueOf(a[2]),Integer.valueOf(a[3])) //here
.collect(Collectors.toList())

Why to map twice? You can directly do this,

.map(s -> {
            String[] parts = s.split(",");
            return new Person(parts[0],parts[1],Integer.valueOf(parts[2]),Integer.valueOf(parts[3]));
        }).collect(Collectors.toList());

I would recommend adding a `static method (or according constructor) to your person class which parses the CSV string:

public static Person fromCSV(String csv) {
    String[] parts = csv.split(",");
    if (parts.length != 4) {
        throw new IllegalArgumentException("csv has not 4 parts");
    }
    return new Person(parts[0], parts[1], Integer.parseInt(parts[2]), Integer.parseInt(parts[3]));
}

To read the lines you can alternatively use Files.lines(). Using all that you can use this to create you List<Person>:

try (Stream<String> lines = Files.lines(Paths.get("./person.txt"))) {
    List<Person> persons = lines
            .map(Person::fromCSV)
            .collect(Collectors.toList());
}

What you did is on correct lines, only thing missing is that you create the Person object in collect, instead you could create it inside the map method itself and return that and use the collect method with Collectors.toList() method. Below code snippet will give a good idea of what I am trying to convey:

al= br.lines()
      .map(s -> {
                    String[] subStrings = s.split(",");
                    return new Person(subStrings[0], subStrings[1], Integer.valueOf(subStrings[2]), Integer.valueOf(subStrings[3]));
                })
      .collect(Collectors.toList());

This way you use the map method only once, and return the object that is needed which the collect method consolidates into a List. If you want it to be an ArrayList you can use Collections framework to cast List to ArrayList, but I think List should be fine for your operations.

Related