我收到技术负责人的请求,要求重写此代码并用通用 lambda 替换 for 循环。我怀疑这会带来更简单、更易读和可维护的代码。
请问有什么好的方法吗?
问题是如何将当前的for循环转换为lambda函数。项目数据结构的更改完全超出范围。查看循环 - 它是状态列表的一个部分,同时检查同一索引处的 addressType 中的值。
如何使用 lambda 做到这一点?它实际上会简化代码吗?
List<String> states = Arrays.asList(item.getState().split(","));
List<String> addressType = Arrays.asList(item.getAddressType().split(","));
List<String> mailingStates = new ArrayList<>();
List<String> physicalStates = new ArrayList<>();
for(int i = 0; i<states.size(); i++){
if(Constants.MAILING.equals(addressType.get(i))){
mailingStates.add(states.get(i));
} else {
physicalStates.add(states.get(i));
}
}
需要说 - 仅限 Java 8
代码将是相同的,所以我不知道重点是什么,但它将使用 lambda 表达式块。
List<String> states = Arrays.asList(item.getState().split(","));
List<String> addressType = Arrays.asList(item.getAddressType().split(","));
List<String> mailingStates = new ArrayList<>(), physicalStates = new ArrayList<>();
IntStream.range(0, states.size()).forEach(i -> {
if (Constants.MAILING.equals(addressType.get(i))) {
mailingStates.add(states.get(i));
} else {
physicalStates.add(states.get(i));
}
});
您想要做的是浏览两个同名列表。此操作的通用名称是“zip” - 当您遍历两个(或有时更多)数组/列表/流/等并对每个元素执行某些操作时。
您可以从这里选择流的实现:使用带有 lambda 的 JDK8 压缩流 (java.util.stream.Streams.zip) 还有许多已经在现有库中实现。如果您的项目中已经有这样的库,则只需导入即可使用它。
出于说明目的,我假设有一个可使用 此签名的实现:
<A, B, C> Stream<C> zip(Stream<? extends A> a,
Stream<? extends B> b,
BiFunction<? super A, ? super B, ? extends C> zipper)
此外,一个好的简单通用实用程序是一个具有两个值的
Pair
类。有许多现有的实现。我将使用此实现此签名可用:
class Pair<LEFT, RIGHT> {
Pair(LEFT left, RIGHT right);
LEFT getLeft();
RIGHT getRight();
}
这将保存相关的状态和地址类型。但您也可以考虑创建一个封装给定状态和地址类型的特定对象。
使用这些通用助手,您的代码可以如下所示:
Stream<String> states = Arrays.stream(item.getState().split(","));
Stream<String> addressType = Arrays.stream(item.getAddressType().split(","));
Map<Boolean, List<String>> splitStates = zip(states, addressTypes,
(state, addressType) -> new Pair<String, String>(state, addressType))
.collect(
Collectors.partitioningBy(pair -> Constants.MAILING.equals(pair.getRight()),
collectors.mapping(pair -> pair.getLeft())
)
);
List<String> mailingStates = split.get(true);
List<String> physicalStates = split.get(false);
如果将 lambda 替换为方法引用并在可能的情况下进行一些小的重新排列,那么您将得到:
private static final Predicate<Pair<String, String> IS_Mailing =
pair -> Constants.MAILING.equals(pair.getRight());
/* ... */
Stream<String> states = Arrays.stream(item.getState().split(","));
Stream<String> addressType = Arrays.stream(item.getAddressType().split(","));
Map<Boolean, List<String>> splitStates = zip(states, addressTypes, Pair::new)
.collect(Collectors.partitioningBy(IS_MAILING),
collectors.mapping(Pair::getLeft()));
List<String> mailingStates = split.get(true);
List<String> physicalStates = split.get(false);
如果你实现一个类而不是通用的
Pair
类:
class StateData {
private String state;
private String addressType;
public StateData(String state, String addressType) {
this.state = state;
this.addressType = addressType;
}
public String getState() { return this.state; }
public String getAddressType() { return this.addressType; }
public boolean isMailing() {
return Constants.MAILING.equals(this.getAddressType());
}
}
代码变得更加语义化:
Stream<String> states = Arrays.stream(item.getState().split(","));
Stream<String> addressType = Arrays.stream(item.getAddressType().split(","));
Map<Boolean, List<String>> splitStates = zip(states, addressTypes, StateData::new)
.collect(Collectors.partitioningBy(StateData::isMailing),
collectors.mapping(StateData::getState()));
List<String> mailingStates = split.get(true);
List<String> physicalStates = split.get(false);
最后一个考虑因素是为
addressType
创建一个枚举,而不是与常量进行比较。
您可以使用
partitioningBy
根据 Constants.MAILING.equals(addressType.get(i))
条件分离出项目。
Map<Boolean, List<Integer>> map
= IntStream.range(0, states.size())
.boxed()
.collect(Collectors.partitioningBy(i -> Constants.MAILING.equals(addressType.get(i))));
List<String> mailingStates = map.get(true);
List<String> physicalStates = map.get(false);