Digital
Digital

Reputation: 591

How to rewrite below code using streams in java8

private void enrichWithFieldList(String desc, String value, List<Data> dataList, List<Field> fieldList) {
        int index = 1;
        for(Data data : dataList){
            if(!"english".equals(data.getCode())){
                createFieldList(desc+index, data.getCode(), fieldList);
                createFieldList(value+index++, data.getValue(), fieldList);
            }
        }
    }

private void createFieldList(String fieldName, String fieldValue, List<Field> fieldList) {
        Field customField = new Field();
        customField.setName(fieldName);
        customField.setValue(fieldValue);
        fieldList.add(customField);
}

Can anybody tell me how to rewrite the above code using Java8 streams?

Upvotes: 2

Views: 600

Answers (3)

sprinter
sprinter

Reputation: 27986

As far as I can tell your trying to come up with a new list of fields based on your data list. If so, probably best to create that directly from a stream rather than add to a list passed into your method.

Something like the following:

private List<Field> makeEnrichedFieldList(String desc, String value, List<Data> dataList) {
    return IntStream.range(0, dataList.size())
        .filter(i -> !dataList.get(i).getCode().equals("english"))
        .boxed ()
        .flatMap(i -> Stream.of(new Field(desc + i + 1, dataList.get(i).getCode()),
                                new Field(value + i + 1, dataList.get(i).getValue()))
        .collect(Collectors.toList());
}

Upvotes: 3

Holger
Holger

Reputation: 298539

Since you are saying that you can’t change the Field class to add a constructor, you should keep your factory method, but you should clean it up by letting it create the instance without dealing with Lists:

private Field createField(String fieldName, String fieldValue) {
    Field customField = new Field();
    customField.setName(fieldName);
    customField.setValue(fieldValue);
    return customField;
}

Then, sprinter’s answer already guides to the right solution. If you need the index, the solution is to use an IntStream of indices and map them to the List elements rather than streaming over the list and trying to reconstitute the index. Then, flatMap is the right operation for mapping an element to two elements. Your condition may be implemented either, as a filter operation before the flatMap or get integrated into the flatMap operation which may map to an empty stream (or just null) if the condition isn’t fulfilled.

There is only one catch. The current API lacks an IntStream.flatMapToObj(…) operation. A simple workaround is to use boxed().flatMap(…), like in sprinter’s answer, which has the disadvantage of boxing overhead, or a mapToObj(…).flatMap(t->t) which maps the int values to Streams object first and then does the flattening on Stream rather than IntStream:

private void enrichWithFieldList(
             String desc, String value, List<Data> dataList, List<Field> fieldList) {
    List<Field> collected=IntStream.rangeClosed(1, dataList.size())
        .filter(index -> !"english".equals(dataList.get(index-1).getCode()))
        .mapToObj(index -> {
            Data data=dataList.get(index-1);
            return Stream.of(createField(desc+index, data.getCode()),
                             createField(value+index, data.getValue()));
        }).flatMap(t->t)
        .collect(Collectors.toList());
    fieldList.addAll(collected);
}

This doesn’t box ints into Integer objects, but has the slight disadvantage of invoking dataList.get(index-1) twice for elements that pass the filter. As said, an alternative is to fuse filter and flatMap:

private void enrichWithFieldList(
             String desc, String value, List<Data> dataList, List<Field> fieldList) {
    List<Field> collected=IntStream.rangeClosed(1, dataList.size())
        .mapToObj(index -> {
            Data data=dataList.get(index-1);
            return "english".equals(data.getCode())? null:
                Stream.of(createField(desc+index, data.getCode()),
                          createField(value+index, data.getValue()));
        }).flatMap(t->t)
        .collect(Collectors.toList());
    fieldList.addAll(collected);
}

But there is another alternative. Since in this case, the intermediate steps don’t really simplify the stream code, we may merge them into the collect operation without loosing readability:

private void enrichWithFieldList(
             String desc, String value, List<Data> dataList, List<Field> fieldList) {
    List<Field> collected=IntStream.rangeClosed(1, dataList.size())
        .collect(ArrayList::new, (list, index) -> {
            Data data=dataList.get(index-1);
            if(!"english".equals(data.getCode())) {
                list.add(createField(desc+index, data.getCode()));
                list.add(createField(value+index, data.getValue()));
            }
        }, List::addAll);
    fieldList.addAll(collected);
}

Then we don’t have a transition from IntStream to Stream at all.

But note that an ordinary loop may still look simpler than that. Unless you have rather large lists to process and are thinking about processing them in parallel, the stream API isn’t a win here.

Upvotes: 1

Peter Lawrey
Peter Lawrey

Reputation: 533870

You can write this, though it's perhaps more complicated.

private List<Field> enrichWithFieldList(String desc, String value, List<Data> dataList) {
    int[] index = { 1 };
    return dataList.stream()
                   .filter(d -> !"english".equals(d.getCode())
                   .flatMap(d -> Stream.of(new Field(desc+index[0], data.getCode()), 
                                           new Field(value+index[0]++, data.getValue())))
                   .collect(Collectors.toList());
 }

Instead of using desc and value I suggest just using a map of code to value.

If you can use a Map, I suggest doing.

private Map<String, String> asMap(Collection<Data> data) {
    return data.stream()
               .filter(d -> !"english".equals(d.getCode())
               .collect(Collectors.toMap(Data:getCode, Data:getValue));
}

Upvotes: 2

Related Questions