【问题标题】:Lambda function with four lists of strings and if-else condition具有四个字符串列表和 if-else 条件的 Lambda 函数
【发布时间】:2021-07-01 15:01:37
【问题描述】:

我收到技术主管的请求,要求我重写此代码并将 for-loop 替换为通用 lambda。我怀疑这会导致更简单、更易读和更可维护的代码。

请问有什么好的方法吗?

问题是关于如何将当前的 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

【问题讨论】:

  • "查看循环 - 它是状态列表的一部分,同时在同一索引处检查 addressType 中的值。" 所以,您只想遍历两个列表同时?看起来很简单 - 制作一个流过两个列表中的每个项目的流。也许使用两个列表的迭代器的Stream.generate()Stream.iterate() 就足够了。然后你只需要消耗成对的元素。
  • 谢谢 VLAZ。
  • 看了一下,似乎没有直接的内置 zip 操作,但here 是如何实现的。 “zip”是遍历两个或多个列表和类似列表的通用名称。通常它是一个接受 lambda 和一堆列表/数组/流/和其他类似东西的函数。

标签: java lambda java-8 stream java-stream


【解决方案1】:

代码将是相同的,所以我不知道重点是什么,但它会使用 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));
            }
        });

【讨论】:

  • 谢谢安德烈亚斯,我同意你的看法,在这个实现中使用 lambda 是没有意义的。
【解决方案2】:

您要做的是浏览两个同名的列表。此操作的通用名称是“zip” - 当您遍历两个(或有时更多)数组/列表/流/等并对每个元素执行某些操作时。

您可以从这里为流选择一个实现:Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip) 现有库中也已经实现了许多。如果您的项目中已经有这样的库,则只需要导入即可使用它。

出于说明目的,我假设this signature 有一个可用的实现:

<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 创建一个枚举,而不是与常量进行比较。

【讨论】:

    【解决方案3】:

    您可以使用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);                             
    

    【讨论】:

      猜你喜欢
      • 2017-03-20
      • 2020-10-17
      • 1970-01-01
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 2020-04-22
      相关资源
      最近更新 更多