替代多个嵌套的 for 和 if 块

Alternative to multiple nested for and if blocks

我正在尝试实现一个我使用多个 for 循环和 if 条件编写的复杂块,而不是那么复杂。初始代码为

for(Coll_Monthly_Main monthlyAccount:monthlyList){
        for(Coll_Daily_Main dailyAccount:dailyList){
            if(monthlyAccount.getAccountId().trim().equals(dailyAccount.getAccountId().trim())){
                for(Catg_Monthly monthlyCategory: monthlyAccount.getCatg()){
                    for(Catg_Daily dailyCategory: dailyAccount.getCatg()){
                        if(monthlyCategory.getPriCatId().trim().equals(dailyCategory.getPriCatId().trim())){
                            monthlyCategory.setMthTtl(dailyCategory.getMthTtl());
                            monthlyCategory.setMtd(dailyCategory.getMtd());
                            monthlyCategory.setYtd(dailyCategory.getYtd());

                            for(SecCatDtl_Mthly monthlySecCategory:monthlyCategory.getSecCatDtl()){
                                for(SecCatDtl_Daily dailySecCategory:dailyCategory.getSecCatDtl()){
                                    if(monthlySecCategory.getCatId().trim().equals(dailySecCategory.getCatId().trim())){
                                        monthlySecCategory.setMthTtl(dailySecCategory.getMthTtl());
                                        monthlySecCategory.setMtd(dailySecCategory.getMtd());
                                        monthlySecCategory.setYtd(dailySecCategory.getYtd());
                                    }
                                }
                            }
                        }

                    }
                }
            }
        }
    }
    return monthlyList;

我已经按照这个 并成功地实现了第一级,如下所示:-

monthlyList.forEach(coll_mthly->{
        dailyList.stream().filter(coll_daily->coll_mthly.getAccountId().trim().equals(coll_daily.getAccountId().trim()))
        .forEach(catg_mth->coll_mthly.getCatg())->{

        };
    });

对于下一级嵌套,我需要遍历嵌套列表,但我不确定如何进行。我不断收到如下语法错误:- 令牌语法错误,应该是 TypeElidedFormalParameter

如果有任何正确方向的指示,我将不胜感激。

更新:- Thomas 的回答是这样的

Map<String, Coll_Daily_Main> dailies = dailyList.stream().collect(Collectors.toMap(cdm -> cdm.getAccountId(), cdm-> cdm) );

    for(Coll_Monthly_Main monthlyAccount : monthlyList) {
        Coll_Daily_Main dailiesForAccount  = dailies.get( monthlyAccount.getAccountId().trim());

          Map<String, Catg_Daily> dailyCatgories=dailiesForAccount.getCatg().stream().collect(Collectors.toMap(cv->cv.getPriCatId(), cv->cv));
          for(Catg_Monthly monthlyCategory:monthlyAccount.getCatg()){
              Catg_Daily dailyCategory = dailyCatgories.get(monthlyCategory.getPriCatId().trim());
              if(dailyCategory!=null){
                monthlyCategory.setMthTtl(dailyCategory.getMthTtl());
                monthlyCategory.setMtd(dailyCategory.getMtd());
                monthlyCategory.setYtd(dailyCategory.getYtd());

                Map<String,SecCatDtl_Daily> dailySecCategories=dailyCategory.getSecCatDtl().stream().collect(Collectors.toMap(fg->fg.getCatId(), fg->fg));
                for(SecCatDtl_Mthly monthlySecCategory:monthlyCategory.getSecCatDtl()){
                    SecCatDtl_Daily dailySecCategory =dailySecCategories.get(monthlySecCategory.getCatId().trim());
                    if(dailySecCategory!=null){
                    monthlySecCategory.setMthTtl(dailySecCategory.getMthTtl());
                    monthlySecCategory.setMtd(dailySecCategory.getMtd());
                    monthlySecCategory.setYtd(dailySecCategory.getYtd());
                    }
                }
              }
          }
        }

正如其他人已经多次指出的那样,最好重新考虑您的方法并使其不仅更具可读性而且更快。我想到的一件事:你有 3 个级别,由 2 个循环和一个 if 组成,用于检查元素是否匹配(通过 id)。这些级别将具有 O(n*m) 复杂性。

但是,您可以尝试以 id 作为键构建一个映射或多映射(Guava 有一些)并将其降低到 O(n + m):

  • O(n) 用于构建地图(理想情况下在较大的集合上,即每天)
  • O(m) 用于迭代第二组(理想情况下是较小的一组,即每月一次)
  • 查找应为 O(1),因此可以忽略

我不确定所有这些嵌套级别是什么意思,所以我只能举一个例子说明你可以为一个级别做些什么(我会选择第一个):

//I'm using Google Guava's classes here
SetMultimap<String, Coll_Daily_Main> dailies = ...;//use one that fits your needs

//Iterate over n daily entries and put them into the map which should be O(n)
dailyList.forEach( cdm -> dailies.put( cdm.getAccountId().trim(), cdm ) );

//Iterate over the (smaller) set of monthly entries and do a lookup for the dailies which should be O(m)
for(Coll_Monthly_Main monthlyAccount : monthlyList) {
  Set<Coll_Daily_Main> dailiesForAccount = dailies.get( monthlyAccount.getAccountId().trim() );

  //level done, either go down to the next or try to further straighten it out or optimize
}

更新:

我忘了说你不必将 Guava 与 Java 8 一起使用。虽然使用 Map<String, Set<Coll_Daily_Main>> 的定义看起来有点尴尬,但不是 "hard"再:

 Map<String, Set<String>> dailies = new HashMap<>();
 dailyList.forEach( cdm -> dailies.computeIfAbsent( cdm.getAccountId().trim(), v -> new HashSet<>() ).add( cdm  ) );

注意:您还可以使用收集器使其更短一些并在一行中。这是否更易于阅读和使用还有待商榷。

Map<String, Set<Daily>> dailies = 
    dailyList.stream().collect( Collectors.groupingBy( cdm -> cdm.getAccountId().trim(), 
                                                       Collectors.toSet() ) );
  • 首先,您需要在私有方法中提取所有 if 语句。
  • 然后你就可以开始用 lambdas 重构你的 for 语句了。

您甚至可以声明一个静态函数(在下面的示例中称为 loop)来导出您的嵌套循环逻辑:

public class Test {

  public List<Coll_Monthly_Main> runThatThing(List<Coll_Monthly_Main> monthlyList, List<Coll_Daily_Main> dailyList) {

    loop(monthlyList, dailyList, Test::updateMonthlyCategories);

    return monthlyList;
  }

  private static void updateMonthlyCategories(Coll_Monthly_Main monthlyAccount, Coll_Daily_Main dailyAccount) {
    if(monthlyAccount.getAccountId().trim().equals(dailyAccount.getAccountId().trim())){
      loop(monthlyAccount.getCatg(), dailyAccount.getCatg(), Test::updateMonthlyCategory);
    }
  }

  private static void updateMonthlyCategory(Catg_Monthly monthlyCategory, Catg_Daily dailyCategory) {
    if(monthlyCategory.getPriCatId().trim().equals(dailyCategory.getPriCatId().trim())){
      monthlyCategory.setMthTtl(dailyCategory.getMthTtl());
      monthlyCategory.setMtd(dailyCategory.getMtd());
      monthlyCategory.setYtd(dailyCategory.getYtd());

      loop(monthlyCategory.getSecCatDtl(), dailyCategory.getSecCatDtl(), Test::updateMonthlySecondCategory);
    }
  }

  private static void updateMonthlySecondCategory(SecCatDtl_Mthly monthlySecCategory, SecCatDtl_Daily dailySecCategory) {
    if(monthlySecCategory.getCatId().trim().equals(dailySecCategory.getCatId().trim())){
      monthlySecCategory.setMthTtl(dailySecCategory.getMthTtl());
      monthlySecCategory.setMtd(dailySecCategory.getMtd());
      monthlySecCategory.setYtd(dailySecCategory.getYtd());
    }
  }

  // nested loops through list1 and list2 which apply the function `f` to all pairs. 
  //Using a BiConsumer because the f methods we use always return void
  private static <T, U> void loop(List<T> list1, List<U> list2, BiConsumer<T, U> f) {
    list1.forEach(
        element1 -> list2.forEach(
            element2 -> f.accept(element1, element2)
        ));
  }
}