【问题标题】:Generate String Permutations from multiple Set values (Java 8 Streams)从多个 Set 值生成字符串排列(Java 8 Streams)
【发布时间】:2018-04-27 04:21:39
【问题描述】:

我有两个集合——国家和州。我想从两者中创建所有可能的排列。

import java.util.*;
import java.util.stream.Collectors;

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello World");

    Set<String> countryPermutations = new HashSet<>(
        Arrays.asList("United States of america", "USA"));
    Set<String> statePermutations = new HashSet<>(
        Arrays.asList("Texas", "TX"));

    Set<String> stateCountryPermutations = countryPermutations.stream()
        .flatMap(country -> statePermutations.stream()
            .flatMap(state -> Stream.of(state + country, country + state)))
        .collect(Collectors.toSet());

    Set<String> finalAliases = Optional.ofNullable(stateCountryPermutations)
        .map(Collection::stream).orElse(Stream.empty())
        .map(sc -> "houston " + sc)
        .collect(Collectors.toSet());
    System.out.println(stateCountryPermutationAliases);
  }
}

州或国家/地区或两者的排列可以为空。我仍然希望我的代码能够正常运行。

要求

  1. 如果状态排列为空,则最终输出应为 [Houston USA, Houston United States of America]

  2. 如果国家/地区排列为空,则最终输出应为 [Houston TX, Houston Texas]

  3. 如果两者都为null,则不输出

我已将代码更改为以下

Set<String> stateCountryPermutations =
    Optional.ofNullable(countryPermutations)
        .map(Collection::stream)
        .orElse(Stream.empty())
        .flatMap(country -> Optional.ofNullable(statePermutations)
            .map(Collection::stream)
            .orElse(Stream.empty())
            .flatMap(state -> Stream.of(state + country, country + state)))
        .collect(Collectors.toSet());

这满足 3。当任一排列为空时,不满足 1 和 2。我没有得到别名作为​​回应。如何修改我的代码?

【问题讨论】:

    标签: java string set java-stream permutation


    【解决方案1】:

    以下代码从任意数量的输入集创建所有组合,忽略空/空集:

    Stream<Collection<String>> inputs = Stream.of(
            Arrays.asList("United States of america", "USA"),
            Arrays.asList("Texas", "TX"),
            Arrays.asList("Hello", "World"),
            null,
            new ArrayList<>());
    
    Stream<Collection<List<String>>> listified = inputs
            .filter(Objects::nonNull)
            .filter(input -> !input.isEmpty())
            .map(l -> l.stream()
                    .map(o -> new ArrayList<>(Arrays.asList(o)))
                    .collect(Collectors.toList()));
    
    Collection<List<String>> combinations = listified
            .reduce((input1, input2) -> {
                Collection<List<String>> merged = new ArrayList<>();
                input1.forEach(permutation1 -> input2.forEach(permutation2 -> {
                    List<String> combination = new ArrayList<>();
                    combination.addAll(permutation1);
                    combination.addAll(permutation2);
                    merged.add(combination);
                }));
                return merged;
            }).orElse(new HashSet<>());
    
    combinations.forEach(System.out::println);
    

    输出:

    [United States of america, Texas, Hello]
    [United States of america, Texas, World]
    [United States of america, TX, Hello]
    [United States of america, TX, World]
    [USA, Texas, Hello]
    [USA, Texas, World]
    [USA, TX, Hello]
    [USA, TX, World]
    

    现在您可以使用您提到的辅助方法来创建每个组合的排列。 This question 展示了如何生成列表的所有排列。

    【讨论】:

      【解决方案2】:

      改写您的问题,据我了解,您有几个集合,我们称它们为标签,并创建所有非null 集合的排列,如果全部为null,则生成一个空流。

      这可以通过简单的逻辑来完成,流过所有集合,过滤掉 null 元素,将它们映射到 Streams 并使用 streamA.stream().flatMap(… -&gt; streamB.map(combiner)) 逻辑将它们简化为单个流,除了流不能多次使用。为了解决这个问题,我们可以通过对流的供应商应用相同的逻辑来实现它。另一个细节是 .map(combiner) 在您的情况下应该是 a -&gt; streamB.flatMap(b -&gt; Stream.of(combine a and b, combine b and a))

      Stream.of(stateLabels, countryLabels) // stream over all collections
            .filter(Objects::nonNull)       // ignore all null elements
            .<Supplier<Stream<String>>>map(c -> c::stream) // map to a supplier of stream
            .reduce((s1,s2) -> // combine them using flatMap and creating a×b and b×a
                () -> s1.get().flatMap(x -> s2.get().flatMap(y -> Stream.of(x+" "+y, y+" "+x))))
            .orElse(Stream::empty) // use supplier of empty stream when all null
            .get() // get the resulting stream
            .map("houston "::concat) // combine all elements with "houston "
            .forEach(System.out::println);
      

      用测试用例演示:

      // testcases
      List<Collection<String>> countryLabelTestCases = Arrays.asList(
          Arrays.asList("United States of america", "USA"),
          null
      );
      List<Collection<String>> stateLabelTestCases = Arrays.asList(
          Arrays.asList("Texas", "TX"),
          null
      );
      for(Collection<String> countryLabels: countryLabelTestCases) {
          for(Collection<String> stateLabels: stateLabelTestCases) {
              // begin test case
              System.out.println(" *** "+(
                  countryLabels==null? stateLabels==null? "both null": "countryLabels null":
                                       stateLabels==null? "stateLabels null": "neither null"
                  )+":"
              );
      
              // actual operation:
      
              Stream.of(stateLabels, countryLabels)
                    .filter(Objects::nonNull)
                    .<Supplier<Stream<String>>>map(c -> c::stream)
                    .reduce((s1,s2) -> () -> s1.get().flatMap(x ->
                                             s2.get().flatMap(y -> Stream.of(x+" "+y, y+" "+x))))
                    .orElse(Stream::empty)
                    .get()
                    .map("houston "::concat)
                    .forEach(System.out::println);
      
              // end of operation
              System.out.println();
          }
      }
      
       *** neither null:
      houston Texas United States of america
      houston United States of america Texas
      houston Texas USA
      houston USA Texas
      houston TX United States of america
      houston United States of america TX
      houston TX USA
      houston USA TX
      
       *** stateLabels null:
      houston United States of america
      houston USA
      
       *** countryLabels null:
      houston Texas
      houston TX
      
       *** both null:
      

      如果您想以列表而不是字符串的形式获取排列,请创建此辅助方法

      static <T> List<T> merge(List<T> a, List<T> b) {
          return Stream.concat(a.stream(), b.stream()).collect(Collectors.toList());
      }
      

      并将流操作更改为

      Stream.of(stateLabels, countryLabels)
            .filter(Objects::nonNull)
            .<Supplier<Stream<List<String>>>>map(c ->
                () -> c.stream().map(Collections::singletonList))
            .reduce((s1,s2) -> () -> s1.get().flatMap(x ->
                                     s2.get().flatMap(y -> Stream.of(merge(x,y), merge(y,x)))))
            .orElse(Stream::empty)
            .get()
            .map(list -> merge(Collections.singletonList("houston"), list))
            // proceed processing the List<String>s
      

      请注意,要支持两个以上的集合,您只需更改Stream.of(stateLabels, countryLabels),插入其他集合即可。

      【讨论】:

        【解决方案3】:

        如果你想创建这样的数据:

        [text1=text2, text1=text3, text2=text3]
        

        这里是如何做到的:

        import java.util.ArrayList;
        import java.util.List;
        import java.util.stream.Collectors;
        
        public class MainTest {
        
            public static void main(String[] args) {
                processData();
            }
        
            public static void processData() {
                List<String> datalist = new ArrayList<>();
                datalist.add("text1");
                datalist.add("text2");
                datalist.add("text3");
                List<String> tempList = new ArrayList<>(datalist);
                List<String> result = datalist.stream()
                            .flatMap(str1 -> getList(tempList, str1).stream().map(str2 -> str1 + "=" +str2))
                            .collect(Collectors.toList());
                System.out.println(result);
            }
        
            private static List<String> getList(List<String> list, String obj){
                list.remove(obj);
                return list;
            }
        }
        

        【讨论】:

          【解决方案4】:

          使用 map 和 reduce 方法的多个非空列表的笛卡尔积。

          Try it online!

          public static void main(String[] args) {
              // a list of lists
              List<List<String>> list = Arrays.asList(
                      Arrays.asList("houston"),
                      Arrays.asList("United States of america", "USA"),
                      Arrays.asList("Texas", "TX"),
                      null, Collections.emptyList());
              // cartesian product
              List<List<String>> cp = cartesianProduct(list);
              // output
              cp.forEach(System.out::println);
          }
          
          /**
           * @param list the input list of lists
           * @param <E>  type of the element of the list
           * @return cartesian product of multiple non-empty lists
           */
          public static <E> List<List<E>> cartesianProduct(List<List<E>> list) {
              // check if not null
              if (list == null) return Collections.emptyList();
              return list.stream()
                      // non-null and non-empty lists
                      .filter(lst -> lst != null && lst.size() > 0)
                      // represent each element of a list as a singleton list
                      .map(lst -> lst.stream().map(Arrays::asList)
                              // Stream<List<List<E>>>
                              .collect(Collectors.toList()))
                      // summation of pairs of list into a single list
                      .reduce((list1, list2) -> list1.stream()
                              // combinations of inner lists
                              .flatMap(inner1 -> list2.stream()
                                      // concatenate into a single list
                                      .map(inner2 -> Stream.of(inner1, inner2)
                                              .flatMap(List::stream)
                                              .collect(Collectors.toList())))
                              // list of combinations
                              .collect(Collectors.toList()))
                      // otherwise an empty list
                      .orElse(Collections.emptyList());
          }
          

          输出:

          [houston, United States of america, Texas]
          [houston, United States of america, TX]
          [houston, USA, Texas]
          [houston, USA, TX]
          

          另见:Generating all possible permutations of a list recursively

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-01-18
            • 1970-01-01
            • 2021-06-22
            • 1970-01-01
            • 2018-03-26
            • 1970-01-01
            • 1970-01-01
            • 2023-03-27
            相关资源
            最近更新 更多