【问题标题】:Find longest consecutive numbers in a list of sorted numbers using java 8 stream使用java 8流在排序数字列表中查找最长的连续数字
【发布时间】:2016-12-27 11:53:30
【问题描述】:

我有一段使用传统 for 循环编写的工作代码,如下所示。我想重构它以使用 java 8 流并删除 for 循环。以下代码的输出应为 3,因为这是最长的连续数字列表(4、5 和 6)

    List<Integer> weekDays = Lists.newArrayList(1, 2, 4, 5, 6);

    List<Integer> consecutiveIntervals = Lists.newArrayList();
    int maxConsecutiveTillNow = 1;
    for (int i = 1; i < weekDays.size(); i++) {
        if (weekDays.get(i) - weekDays.get(i - 1) == 1) {
            maxConsecutiveTillNow++;
        } else {
            consecutiveIntervals.add(maxConsecutiveTillNow);
            maxConsecutiveTillNow = 1;
        }
    }
    consecutiveIntervals.add(maxConsecutiveTillNow);

    System.out.println(consecutiveIntervals.stream()
                                           .max(Integer::compareTo)
                                           .get()
                      );

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    Stream API 不太适合此类问题。几乎不可能以正确的方式跟踪看到的 Stream 元素。所以你必须去多个Streams

    基于Stuart Marks' 答案之一。

    List<Integer> weekDays = Arrays.asList(1, 2, 4, 5, 6);
    int[] indices = IntStream.rangeClosed(0, weekDays.size())
       .filter(i -> i == 0 || i == weekDays.size() || weekDays.get(i - 1) + 1 != weekDays.get(i))
       .toArray();
    int longest = IntStream.range(0, indices.length - 1).map(i -> indices[i + 1] - indices[i])
       .max().orElseThrow(NoSuchElementException::new);
    System.out.println(longest);
    

    输出

    3

    【讨论】:

      【解决方案2】:

      好吧,流的想法是彼此独立地处理流中的项目。该模式的最大好处是您可以并行化执行,因为流 API 的设计是并发的(即您不应该在流的顺序项的处理之间保持状态)。在您的特定任务中,最适合使用数组。

      【讨论】:

      • 我知道你想说什么。但我想使用 java 8 流来解决它,即使它不是最理想的解决方案。
      【解决方案3】:

      创建序列列表并将其排序为所有序列列表中最长的序列。

      例如如果有一个List[1,2,4,5,6],它被分解成:

      [[1,2], [4,5,6]]

      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      import java.util.Optional;
      import java.util.function.Supplier;
      
      public class NumberSequence {
          public static void main(String[] args) {
              List<Integer> weekDays = Arrays.asList(1, 2, 4, 5, 6);
      
              List<List<Integer>> allWeekdaySequences = weekDays.stream()
                                                                .collect(
                                                                          (Supplier<List<List<Integer>>>) ArrayList::new,
                                                                          (sequences, currentElement) -> generateCombinedBatchesFor(sequences, currentElement),
                                                                          List::addAll);
      
              System.out.println(allWeekdaySequences);
      
              Optional<List<Integer>> longestSequence = allWeekdaySequences.stream()
                                                                 .sorted((seq1, seq2) -> seq2.size() - seq1.size())
                                                                 .findFirst();
      
              System.out.println(longestSequence.isPresent() ? longestSequence.get().size() : "Nothing found!!");
          }
      
          private static void generateCombinedBatchesFor(List<List<Integer>> sequences, Integer currentElement) {
              if (needsNewSequence(sequences, currentElement)) {
                  sequences.add(new ArrayList<>());
              }
              getLastSequence(sequences).add(currentElement);
          }
      
          private static boolean needsNewSequence(List<List<Integer>> sequences, Integer currentElement) {
              return sequences.size() == 0 || !isConsecutiveDay(getLastElement(getLastSequence(sequences)),
                      currentElement);
          }
      
          private static boolean isConsecutiveDay(Integer lastElement, Integer currentElement) {
              return currentElement - lastElement == 1;
          }
      
          private static Integer getLastElement(List<Integer> lastSequence) {
              return !lastSequence.isEmpty() ? lastSequence.get(lastSequence.size()-1) : -1;
          }
      
          private static List<Integer> getLastSequence(List<List<Integer>> sequences) {
              return sequences.get(sequences.size()-1);
          }
      }
      

      输出

      3

      【讨论】:

        【解决方案4】:

        为此,您需要进行部分缩减(批处理),这是 java 8 流 API 不提供的。

        您可以通过拆分器(类似于 this answer)自己实现它,也可以使用提供它的库,例如 streamex 或 proton-pack

        【讨论】:

          【解决方案5】:

          如果您不介意使用第三方库,我可能会推荐我的免费 StreamEx 库,它增强了标准 Stream API 并提供了用于部分缩减的运算符:

          StreamEx.of(weekDays)
                  .collapse((a, b) -> b - a == 1, Collectors.counting())
                  .max(Comparator.naturalOrder())
          

          这里的collapse 是一个部分归约运算符。第一个BiPredicate 告诉给定的相邻元素对是应该一起减少它们还是应该开始新的减少组。这里的规则是相邻元素应该相差 1。第二个参数是用于执行归约的收集器(此处为 counting()collector)。所以在collapse 之后,我们有组长度的流(2 和 3 用于您的输入)。最后我们使用标准的max() 运算符。

          StreamEx 库与 Stream API 完全兼容(StreamEx 创建的 Stream 可以作为普通流使用)。 collapse 操作符是惰性的,非常快速且并行友好。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-05-19
            • 2019-09-15
            • 1970-01-01
            • 1970-01-01
            • 2011-11-19
            • 1970-01-01
            相关资源
            最近更新 更多