Reputation: 591
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
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
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 List
s:
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 Stream
s 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 int
s 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
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