如何创建类似于三个不同类型列表的笛卡尔积的数据结构?

How to create a data structure similar to the cartesian product of three lists of different types?

我想创建一个类似于三个列表的笛卡尔积的数据结构。 我还参考了 的现有答案,建议使用 flatMap。我也尝试过这种方式。但我的条件是 filterValue 列表在 types 列表中。所以 flatMap 在这里不起作用。因为 filterValues 可以是 0 or more。因此,根据笛卡尔积(我们可能称之为组合)会发生变化。

每个列表的 measures, types & filterValues 大小可能不同。 如果 measure 列表为空。然后组合将只有 types & filterValues(并且 measure 将设置为 null。我在 if-else 块 [=35= 的评论中添加了这些不同的场景]

我有以下类型的列表:

  1. List<String> measures
  2. List<Type> types
  3. List<FilterValue> filterValues

例如输入结构是:

{
  "measures": [
    "m1",
    "m2",
    "m3"
  ],
  "types": [
    {
      "type": "type-1",
      //some more fields
      "filterValues": [
        //no filter values present
      ]
    },
    {
      "type": "type-2",
      //some more fields
      "filterValues": [
        {
          "filterValue": "t2f1"
          //some more fields
        },
        {
          "filterValue": "t2f2"
          //some more fields
        }
      ]
    }
  ]
}

那么在上面的例子中,我期望的输出数据结构是

m1  type-1 null
m1  type-2 t2f1 
m1  type-2 t2f2 

m2  type-1 null
m2  type-2 t2f1 
m2  type-2 t2f2 

m3  type-1 null
m3  type-2 t2f1 
m3  type-2 t2f2 

然后我将上面的相同值设置为以下 classes:

class SearchArea {
    String measure;
    String type;
    TypeCombi typeFileter;
    //constructor for measure & type
    //constructor for all three
    //getters & setters
}

class TypeCombi {
    String type;
    String name; //it is mapped with filterValue
    //constructor for above two fields
    //getters & setters
}

classType&FilterValue如下

class Type {
    String type;
    List<FilterValue> filterValues;
    //some more fields
    //getters and setters
}

class FilterValue {
    String filterValue;
    //some more fields
    //getters and setters
}

我能够使用以下 getSearchAreas 函数实现预期的输出。但在这种情况下,我使用了多个(两个)for 循环。这个代码块可以使用 stream/flatmap 而不是两个 for loops 清理吗? 还有没有更好的方法来处理多个 if/else 块?(我已经在每个 if/else 块上方添加了注释以适应它的场景)

private List<SearchArea> getSearchAreas(List<String> measures, List<Type> types){
    List<SearchArea> searchAreas = new ArrayList<>();

    //measures & types both are empty
    if ((measures == null || measures.isEmpty())
            && (types == null || types.isEmpty()))
        return Collections.emptyList();

    //one or more measure and zero types
    else if (measures != null && !measures.isEmpty()
            && (types == null || types.isEmpty())) {
        searchAreas = measures
                .stream()
                .map(measure -> new SearchArea(measure, null))
                .collect(Collectors.toList());
        return searchAreas;
    }
    //zero measures and one or more types
    else if ((measures == null || measures.isEmpty())) {
        for (type type : types) {
            if (type.getFilterValues() == null
                    || type.getFilterValues().isEmpty()) {
                searchAreas.add(new SearchArea(null, type.getType()));
            } else {
                searchAreas.addAll(type.getFilterValues()
                        .stream()
                        .map(filterValue -> new SearchArea(null,
                                type.getType(),
                                new TypeCombi(type.getType(),
                                        filterValue.getFilterValue())))
                        .collect(Collectors.toList()));
            }
        }
        return searchAreas;
    }
    //one or more measures and one or more types
    else {
        for (String measure : measures) {
            for (Type type : types) {
                if (type.getFilterValues() == null
                        || type.getFilterValues().isEmpty()) {
                    searchAreas.add(new SearchArea(measure, type.getType()));
                } else {
                    searchAreas.addAll(type.getFilterValues()
                            .stream()
                            .map(filterValue -> new SearchArea(measure,
                                    type.getType(),
                                    new TypeCombi(type.getType(),
                                            filterValue.getFilterValue())))
                            .collect(Collectors.toList()));
                }
            }
        }
        return searchAreas;
    }
}

如果有人能帮助我以更清洁的方式进行上述重组,那就太好了。

我想这就是你想要的。请注意,有时不使用流会更干净。

public static void main(String[] args) throws Exception {
    List<String> strings = Collections.emptyList();
    List<Integer> ints = Arrays.asList(1, 2, 3);

    if (strings == null || strings.isEmpty()) {
        strings = Collections.singletonList(null);
    }

    if (ints == null || ints.isEmpty()) {
        ints = Collections.singletonList(null);
    }

    for (String str : strings) {
        for (Integer integer : ints) {
            // In your code doubles comes from a property of integer.
            List<Double> doubles = integer == null ? Collections.emptyList() : Arrays.asList(1.0d, 2.0d, 3.0d);

            if (doubles == null || doubles.isEmpty()) {
                doubles = Collections.singletonList(null);
            }

            for (Double doubler : doubles) {
                // Create your object here.
                System.out.format(Locale.US, "    str = %s, int = %d, double = %f %n", str, integer, doubler);
            }
        }
    }
}

输出如下:

str = null, int = 1, double = 1.000000 
str = null, int = 1, double = 2.000000
str = null, int = 1, double = 3.000000
str = null, int = 2, double = 1.000000
str = null, int = 2, double = 2.000000 
str = null, int = 2, double = 3.000000
str = null, int = 3, double = 1.000000
str = null, int = 3, double = 2.000000
str = null, int = 3, double = 3.000000

您可以获得三个或更多不同类型列表的笛卡尔积,并将其存储到列表列表对象List<List<Object>>.

public static List<List<Object>> cartesianProduct(List<?>... lists) {
    // incorrect incoming data
    if (lists == null) return Collections.emptyList();
    return Arrays.stream(lists)
            // non-null and non-empty lists
            .filter(list -> list != null && list.size() > 0)
            // represent each list element as SingletonList<Object>
            .map(list -> list.stream().map(Collections::<Object>singletonList)
                    // Stream<List<List<Object>>>
                    .collect(Collectors.toList()))
            // summation of pairs of inner lists
            .reduce((list1, list2) -> list1.stream()
                    // combinations of inner lists
                    .flatMap(inner1 -> list2.stream()
                            // merge two inner lists into one
                            .map(inner2 -> Stream.of(inner1, inner2)
                                    .flatMap(List::stream)
                                    .collect(Collectors.toList())))
                    // list of combinations
                    .collect(Collectors.toList()))
            // returns List<List<Object>>, otherwise an empty list
            .orElse(Collections.emptyList());
}
public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2);
    List<String> list2 = Arrays.asList("A", "B");
    List<Object> list3 = Arrays.asList(null, "NULL");
    List<Time> list4 = Collections.singletonList(new Time(0));

    List<List<Object>> lists = cartesianProduct(list1, list2, list3, list4);
    // output
    lists.forEach(System.out::println);
}

输出:

[1, A, null, 03:00:00]
[1, A, NULL, 03:00:00]
[1, B, null, 03:00:00]
[1, B, NULL, 03:00:00]
[2, A, null, 03:00:00]
[2, A, NULL, 03:00:00]
[2, B, null, 03:00:00]
[2, B, NULL, 03:00:00]

另请参阅:Find cartesian product of 2 lists

您可以创建一个通用方法,它接受不同类型的列表 List<? extends R> 和 returns 它们的超类型列表 List<R>

Try it online!

/**
 * @param lists a list of lists for multiplication
 * @param <R>   the supertype of the elements
 * @return the Cartesian product
 */
public static <R> List<List<R>> cartesianProduct(List<List<? extends R>> lists) {
    // check if incoming data is not null
    if (lists == null) return Collections.emptyList();
    // Cartesian product, intermediate result
    List<List<R>> cp = Collections.singletonList(Collections.emptyList());
    // iterate through incoming lists
    for (List<? extends R> list : lists) {
        // non-null and non-empty lists
        if (list == null || list.size() == 0) continue;
        // intermediate result for next iteration
        List<List<R>> next = new ArrayList<>();
        // rows of current intermediate result
        for (List<R> row : cp) {
            // elements of current list
            for (R el : list) {
                // new row for next intermediate result
                List<R> nRow = new ArrayList<>(row);
                nRow.add(el);
                next.add(nRow);
            }
        }
        // pass to next iteration
        cp = next;
    }
    // Cartesian product, final result
    return cp;
}
public static void main(String[] args) {
    List<Integer> l1 = Arrays.asList(1, 2);
    List<Long> l2 = Arrays.asList(3L, 4L);
    List<Double> l3 = Arrays.asList(5.5D, 6.6D);

    List<List<Number>> cp = cartesianProduct(Arrays.asList(l1, l2, l3));
    // output
    for (List<Number> row : cp) System.out.println(row);
}

输出:

[1, 3, 5.5]
[1, 3, 6.6]
[1, 4, 5.5]
[1, 4, 6.6]
[2, 3, 5.5]
[2, 3, 6.6]
[2, 4, 5.5]
[2, 4, 6.6]

另请参阅:How to get Cartesian product from multiple lists?