简体   繁体   中英

java 8 i/o stream application

I have a CSV file below and I wrote the method below to parse the file.

"PRODUCT_NAME","PRODUCT_CATEGORY","SALES_PRICE"
"Hair Gel","Beauty",6.99 
"Scotch tape","Office Supply",2.99
"Office Chair","Office Supply",134.99

My code:

public static void ReadFile(String path, Map<String, List<Double>> db){
    try(BufferedReader br = new BufferedReader(new FileReader(path))){
        String line;
        if((line = br.readLine()) != null){
            //this line is the heading..and needed to be skipped
        }
        while ((line = br.readLine()) != null) {
            String[] keys = line.split(",");
            keys[1] = keys[1].replace("\"", "");
            if(db.containsKey(keys[1])){
                List<Double> list = db.get(keys[1]);
                list.add(Double.valueOf(keys[2]));
                db.put(keys[1], list);
            }
            else{
                List<Double> list = new ArrayList<>();
                list.add(Double.valueOf(keys[2]));
                db.put(keys[1],list);
            }
        }

    }catch(IOException e){}
}

It works fine in parsing the file but my main question is how could the above method be written taking advantage of java8 stream and lambda functionality?

You can retrieve a Stream over the lines of the file with Files.lines(path) (and you can get a Path with Paths.get ). The header line is skipped with skip(1) , then each line is split around "," .

Finally, the Stream element are grouped by the first element of the keys (with quotes removed) and the values are mapped to the Double value of the second element of the keys and collected into a List . The group operation is done with the groupingBy(classifier, downstream) collector: the classifier is a method that returns the key to group by and the downstream collector collects all Stream elements grouped to the same key; in this case, for the values having the same key, we want to map the Stream element to the Double value ( mapping ) and collects those Double into a List ( toList() ).

The problem in your design is that your method takes a Map<String, List<Double>> db as parameter and mutates it when it is better to let the method return it.

public static Map<String, List<Double>> readFile(String path) throws IOException {
    try (Stream<String> stream = Files.lines(Paths.get(path))) {
        return stream.skip(1)
                  .map(l -> l.split(","))
                  .collect(Collectors.groupingBy(
                    keys -> keys[1].replace("\"", ""),
                    Collectors.mapping(keys -> Double.valueOf(keys[2]), Collectors.toList())
                  ));
    }
}

Note that I also modified your code to let it throw the IOException instead of catching it doing nothing with it. If you want to handle that exception in the method itself, you can still add the catch part.

You can use the Stream API like this

Map<String, List<Double>> rows = Files.lines(Paths.get("file"))
        .skip(1)
        .map(s -> s.split("\"?,\"?"))
        .collect(Collectors.groupingBy(s -> s[1], 
                 Collectors.mapping(s -> Double.parseDouble(s[2]), Collectors.toList())));

The main trick is skipping the first entry.

You could use the BufferedReader.lines , which returns a Stream<String> with the lines of the file. You could, at first instance, replace the I/O code with it:

try(Stream<String> fileLines = Files.lines(Paths.get(path))){
    fileLines.forEach(line -> {
        // Your code inside the "while" loop would go here
    });
}catch(IOException e){}

After this first transformation, you could rethink the structure of your "parsing" code to maybe put it into a more functional structure using operations such as "filter", "map" and the like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM