通常情况下,在尝试重构代码时,你应该只关注必要的更改。
仅仅因为你将使用Stream API,并没有理由在代码中添加检查空或空数组的代码,这些代码在基于循环的代码中并不存在。你也不应该将BigInteger更改为Integer。
然后,你有两个不同的输入,想要从它们中获取不同的结果,换句话说,你有两个完全不同的操作。虽然考虑共享公共代码是合理的,但一旦确定了相同的代码,就没有必要将两个完全不同的操作表达为单个操作。
首先,让我们看看如何在传统循环中实现此操作:
static void addToLists(String id, List<Integer> empIdList, List<BigInteger> dateList) {
String[] array = id.split("-");
dateList.add(new BigInteger(array[1]));
empIdList.add(Integer.valueOf(array[2]));
}
List<Integer> empIdAccepted = new ArrayList<>();
List<BigInteger> dateAccepted = new ArrayList<>();
for(EmployeeValidationAccepted acceptedDetail : acceptedDetails) {
addToLists(acceptedDetail.getId(), empIdAccepted, dateAccepted);
}
List<Integer> empIdRejected = new ArrayList<>();
List<BigInteger> dateRejected = new ArrayList<>();
for(EmployeeValidationRejected rejectedDetail : rejectedDetails) {
addToLists(rejectedDetail.getAd().getId(), empIdRejected, dateRejected);
}
如果我们想要表达与流操作相同的内容,那么每个操作都会有两个结果的障碍。直到 JDK 12 才推出了内置解决方案:
static Collector<String,?,Map.Entry<List<Integer>,List<BigInteger>>> idAndDate() {
return Collectors.mapping(s -> s.split("-"),
Collectors.teeing(
Collectors.mapping(a -> Integer.valueOf(a[2]), Collectors.toList()),
Collectors.mapping(a -> new BigInteger(a[1]), Collectors.toList()),
Map::entry));
}
Map.Entry<List<Integer>, List<BigInteger>> e;
e = Arrays.stream(acceptedDetails)
.map(EmployeeValidationAccepted::getId)
.collect(idAndDate());
List<Integer> empIdAccepted = e.getKey();
List<BigInteger> dateAccepted = e.getValue();
e = Arrays.stream(rejectedDetails)
.map(r -> r.getAd().getId())
.collect(idAndDate());
List<Integer> empIdRejected = e.getKey();
List<BigInteger> dateRejected = e.getValue();
由于一个方法无法返回两个值,因此使用 Map.Entry
来保存它们。
如果要在 JDK 12 之前的 Java 版本中使用此解决方案,可以使用 this answer 末尾发布的实现。然后,您还必须将 Map::entry
替换为 AbstractMap.SimpleImmutableEntry::new
。
或者,您可以使用为此特定操作编写的自定义收集器:
static Collector<String,?,Map.Entry<List<Integer>,List<BigInteger>>> idAndDate() {
return Collector.of(
() -> new AbstractMap.SimpleImmutableEntry<>(new ArrayList<>(), new ArrayList<>()),
(e,id) -> {
String[] array = id.split("-");
e.getValue().add(new BigInteger(array[1]));
e.getKey().add(Integer.valueOf(array[2]));
},
(e1, e2) -> {
e1.getKey().addAll(e2.getKey());
e1.getValue().addAll(e2.getValue());
return e1;
});
}
换句话说,使用Stream API并不总是能使代码更简单。
最后需要注意的是,我们不需要使用Stream API来利用lambda表达式。我们也可以使用它们将循环移入公共代码中。
static <T> void addToLists(T[] elements, Function<T,String> tToId,
List<Integer> empIdList, List<BigInteger> dateList) {
for(T t: elements) {
String[] array = tToId.apply(t).split("-");
dateList.add(new BigInteger(array[1]));
empIdList.add(Integer.valueOf(array[2]));
}
}
List<Integer> empIdAccepted = new ArrayList<>();
List<BigInteger> dateAccepted = new ArrayList<>();
addToLists(acceptedDetails, EmployeeValidationAccepted::getId, empIdAccepted, dateAccepted);
List<Integer> empIdRejected = new ArrayList<>();
List<BigInteger> dateRejected = new ArrayList<>();
addToLists(rejectedDetails, r -> r.getAd().getId(), empIdRejected, dateRejected);
empIdAccepted
和dateAccepted
等各自使用一个stream.map()等。因为这样更清晰,并将您展示的逻辑分解成不相关的不同方面。这是函数式编程的基础:将其分解为子操作或步骤。 - roookeee