如何在Java中创建三种不同类型的对象列表的笛卡尔积数据结构?

问题描述

我想创建一个类似于三个List的笛卡尔积的DataStructure。 我还参考了Jurgen的现有答案,建议使用flatMap。我也尝试过这种方式。但是我已经确定filterValue列表位于types列表内。因此,flatMap在这里不起作用。由于filterValues可以是0 or more。因此,根据笛卡尔积(我们可以称其为组合),它会发生变化。

每个列表的measures,types & filterValues大小可以不同。 如果measure列表为空。然后,组合将只有types & filterValues(并且measure将设置为null。我在if-else块的注释中添加了这些不同的方案

我有以下几种类型的列表:

  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 

然后将相同的上述值设置为以下类别:

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
}

TypeFilterValue如下

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,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,double = 2.000000
str = null,double = 3.000000
str = null,int = 2,double = 1.000000
str = null,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,NULL,B,03:00:00]
[2,03:00:00]

另见:Find cartesian product of 2 lists

,

您可以创建一个通用方法,该方法接受不同类型 List<? extends R> 的列表并返回其超类型 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,6.6]
[1,4,6.6]
[2,5.5]
[2,6.6]

另见:How to get Cartesian product from multiple lists?