【问题标题】:Bubble sort in functional style Java 8Java 8 中的函数式冒泡排序
【发布时间】:2015-02-22 17:49:07
【问题描述】:

您将如何以函数式 (Java 8) 方式实现以下冒泡排序算法?

public static final <T extends Comparable<T>> List<T> imperativeBubbleSort(List<T> list) {
    int len = list == null ? 0 : list.size();
    for (int j = len - 1; j > 0; j--) {
        for (int k = 0; k < j; k++) {
            if (list.get(k + 1).compareTo(list.get(k)) < 0) {
                list.add(k, list.remove(k + 1));
            }
        }
    }
    return list;
}

【问题讨论】:

  • 冒泡排序通常不适合函数式编程。在这种情况下,列表的合并排序和堆排序更自然。

标签: java sorting java-8 functional-programming bubble-sort


【解决方案1】:

我有一个可行的方法:

@SuppressWarnings("unchecked")
public static final <T extends Comparable<T>> List<T> declarativeBubbleSort(final List<T> list) {
    List<T> result = new ArrayList<>(list);
    int len = result.size();
    Function<Function<Object, Object>, IntConsumer> consumer =
            recur -> length -> IntStream.range(0, length)
                    .filter(i -> IntStream.range(0, len - i - 1)
                            .filter(j -> result.get(j + 1).compareTo(result.get(j)) < 0)
                            .mapToObj(j -> {
                                T swap = result.remove(j + 1);
                                result.add(j, swap);
                                return swap;
                            }).count() > 0)
                    .max().ifPresent(IntConsumer.class.cast(recur.apply(Function.class.cast(recur))));
    consumer.apply(Function.class.cast(consumer)).accept(len);
    return result;
}

我知道我仍然有点命令式,但对于这种类型,我发现很难在 Java 中做到完全声明式而不影响性能。

如果要并行,那么:

@SuppressWarnings("unchecked")
public static final <T extends Comparable<T>> List<T> declarativeParallelBubbleSort(final List<T> list) {
    List<T> result = new ArrayList<>(list);
    int len = result.size();
    Function<Function<Object, Object>, IntConsumer> consumer =
            recur -> length -> IntStream.range(0, length)
                    .filter(i -> IntStream.range(0, len - i - 1)
                            .filter(j -> result.get(j + 1).compareTo(result.get(j)) < 0)
                            .parallel()
                            .mapToObj(j -> {
                                synchronized (result) {
                                    T swap = result.remove(j + 1);
                                    result.add(j, swap);
                                    return swap;
                                }
                            }).count() > 0)
                    .max().ifPresent(IntConsumer.class.cast(recur.apply(Function.class.cast(recur))));
    consumer.apply(Function.class.cast(consumer)).accept(len);
    return result;
}

【讨论】:

    【解决方案2】:

    使用 Java 8 api 的简化版本:

    public static int[] bubbleSort(int[] array) {
        BiConsumer<int[], Integer> swapValueIf = (a, j) -> {
            if (a[j] > a[j + 1]) {
                int temp = a[j];
                array[j] = a[j + 1];
                array[j + 1] = temp;
            }
        };
    
        IntStream.range(0, array.length - 1)
                .forEach(i -> IntStream.range(0, array.length - 1)
                        .forEach(j -> swapValueIf.accept(array, j)));
        return array;
    }
    

    【讨论】:

      【解决方案3】:

      具有两个嵌套循环的算法Bubble sort with step-by-step output


      带有逐步输出 Java 8 的冒泡排序

      您可以将两个嵌套循环替换为两个嵌套流。内部流通过列表,比较相邻元素并返回交换次数。并且外部流重复passes,直到内部流中没有任何东西可以swap

      public static void main(String[] args) {
          LinkedList<Integer> list = new LinkedList<>();
          Collections.addAll(list, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
          bubbleSort8(list);
      }
      
      public static void bubbleSort8(List<Integer> list) {
          // counters: 0-passes, 1-swaps
          int[] counter = new int[2];
          IntStream.iterate(0, i -> i + 1)
              // output the beginning of the pass and increase the counter of passes
              .peek(i -> System.out.print((i==0?"<pre>":"<br>")+"Pass: "+counter[0]++))
              // repeat the passes through the list until
              // all the elements are in the correct order
              .anyMatch(i -> IntStream
                  // pass through the list and
                  // compare adjacent elements
                  .range(0, list.size() - 1)
                  // if this element is greater than the next one
                  .filter(j -> list.get(j) > list.get(j + 1))
                  // then swap them
                  .peek(j -> Collections.swap(list, j, j + 1))
                  // output the list and increase the counter of swaps
                  .peek(j -> System.out.print(outputSwapped8(list,j,j+1,counter[1]++)))
                  // if there are no swapped elements at the
                  // current pass, then this is the last pass
                  .count() == 0);
          // output total
          System.out.print("<br>Total: Passes=" + counter[0]);
          System.out.println(", swaps=" + counter[1] + "</pre>");
      }
      
      static String outputSwapped8(List<Integer> list, int e1, int e2, int counter) {
          return IntStream.range(0, list.size())
                  .mapToObj(i -> i == e1 || i == e2 ?
                          // swapped elements are in bold
                          "<b>" + list.get(i) + "</b>" :
                          // other elements
                          "" + list.get(i))
                  .collect(Collectors.joining(" ", "<br>", " | " + counter));
      }
      

      输出:

      通过:0
      9 10 8 7 6 5 4 3 2 1 | 0
      9 8 10 7 6 5 4 3 2 1 | 1
      9 8 7 10 6 5 4 3 2 1 | 2
      9 8 7 6 10 5 4 3 2 1 | 3
      9 8 7 6 5 10 4 3 2 1 | 4
      9 8 7 6 5 4 10 3 2 1 | 5
      9 8 7 6 5 4 3 10 2 1 | 6
      9 8 7 6 5 4 3 2 10 1 | 7
      9 8 7 6 5 4 3 2 1 10 | 8
      通过:1
      8 9 7 6 5 4 3 2 1 10 | 9
      8 7 9 6 5 4 3 2 1 10 | 10
      8 7 6 9 5 4 3 2 1 10 | 11
      8 7 6 5 9 4 3 2 1 10 | 12
      8 7 6 5 4 9 3 2 1 10 | 13
      8 7 6 5 4 3 9 2 1 10 | 14
      8 7 6 5 4 3 2 9 1 10 | 15
      8 7 6 5 4 3 2 1 9 10 | 16
      通过:2
      7 8 6 5 4 3 2 1 9 10 | 17
      7 6 8 5 4 3 2 1 9 10 | 18
      7 6 5 8 4 3 2 1 9 10 | 19
      7 6 5 4 8 3 2 1 9 10 | 20
      7 6 5 4 3 8 2 1 9 10 | 21
      7 6 5 4 3 2 8 1 9 10 | 22
      7 6 5 4 3 2 1 8 9 10 | 23
      通过:3
      6 7 5 4 3 2 1 8 9 10 | 24
      6 5 7 4 3 2 1 8 9 10 | 25
      6 5 4 7 3 2 1 8 9 10 | 26
      6 5 4 3 7 2 1 8 9 10 | 27
      6 5 4 3 2 7 1 8 9 10 | 28
      6 5 4 3 2 1 7 8 9 10 | 29
      通过:4
      5 6 4 3 2 1 7 8 9 10 | 30
      5 4 6 3 2 1 7 8 9 10 | 31
      5 4 3 6 2 1 7 8 9 10 | 32
      5 4 3 2 6 1 7 8 9 10 | 33
      5 4 3 2 1 6 7 8 9 10 | 34
      通过:5
      4 5 3 2 1 6 7 8 9 10 | 35
      4 3 5 2 1 6 7 8 9 10 | 36
      4 3 2 5 1 6 7 8 9 10 | 37
      4 3 2 1 5 6 7 8 9 10 | 38
      通过:6
      3 4 2 1 5 6 7 8 9 10 | 39
      3 2 4 1 5 6 7 8 9 10 | 40
      3 2 1 4 5 6 7 8 9 10 | 41
      通过:7
      2 3 1 4 5 6 7 8 9 10 | 42
      2 1 3 4 5 6 7 8 9 10 | 43
      通过:8
      1 2 3 4 5 6 7 8 9 10 | 44
      通过:9
      总计:通过=10,交换=45

      另见:Bubble sort algorithm for a linked list

      【讨论】:

        【解决方案4】:

        我能想到的最短方法是跟随。其中 listForBubbleSort 是输入,bubbleSorted 是输出。

        List<Integer> listForBubbleSort = Arrays.asList(5, 4, 3, 7, 6, 9, 11, 10, 21);
        
        final List<Integer> copiedList = new ArrayList<>(listForBubbleSort);
        copiedList.add(Integer.MAX_VALUE);
        
        final List<Integer> bubbleSorted = new ArrayList<>();
        
        copiedList.stream().reduce((c, e) -> {
            if (c < e) {
                bubbleSorted.add(c);
                return e;
            } else {
                bubbleSorted.add(e);
                return c;
            }
        });
        
        System.out.println(bubbleSorted); // [4, 3, 5, 6, 7, 9, 10, 11, 21]
        

        我仍然觉得,如果我们可以创建一个自定义收集器并将收集器传递给流的收集器,那就太好了。就像我们将 collect(toList()) 传递给流一样。但仍在学习 Java 8,因此需要更多时间来创建相同的内容。 如果有人已经为此创建了自定义收集器,请分享。

        【讨论】:

        • 是的,事实上我发现尝试使用函数式解决这个算法是违反函数式编程概念的。
        • 该解决方案使用 Java8 功能,但它不起作用,因为您具有可变状态(您正在向可变列表添加元素)。
        【解决方案5】:

        我认为 Java 8 在这种情况下不会为以函数式样式编写冒泡排序提供太多帮助。

        例如这个Haskell中冒泡排序的实现implementation可以在Java中模拟如下。它更实用,因为它使用递归而不是迭代,但 Java 8 仍然缺乏 模式匹配、列表连接等功能,以更实用的风格表达算法。

        public static final <T extends Comparable<T>> List<T> functionalBubbleSort(List<T> list) {
            for (int i = 0; i < list.size(); i++) {
                list = onePassSort(list);
            }
            return list;
        }
        
        public static final <T extends Comparable<T>> List<T> onePassSort(List<T> list) {
            if (list.size() == 0 || list.size() == 1) { 
                return list;
            } else {
                T first = list.get(0);
                T second = list.get(1);
                if (first.compareTo(second) < 0) {
                    return merge(first, onePassSort(list.subList(1, list.size())));
                } else {
                    return merge(second, onePassSort(merge(first, list.subList(2, list.size()))));
                }
            }
        }
        
        public static <T> List<T> merge(T head, List<T> tail) {
            List<T> result = new ArrayList<>();
            result.add(head);
            result.addAll(tail);
            return result;
        }
        

        【讨论】:

          【解决方案6】:

          这取决于您所说的功能性。如果您的意思只是将函数作为第一类对象传递,那么您应该将方法签名更改为:

          public static final <T> List<T> imperativeBubbleSort(List<T> list, Comparator<T> comparisonFunction)
          

          这样比较逻辑可以作为参数提供。

          如果您的意思是完全功能化而不是程序化,那么我将其称为反模式。尽管您可能听说过,Java 8 并不完全支持函数式编程。它缺少的一个关键特性是尾调用优化。没有它,定义函数式编程的那种无循环编程很可能会因为相对较小的值而使您的 JVM 崩溃。

          更多关于尾调用优化和 JVM 的信息可以在这里找到:http://www.drdobbs.com/jvm/tail-call-optimization-and-java/240167044

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-05-10
            • 1970-01-01
            • 2014-06-25
            • 2017-06-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-03
            相关资源
            最近更新 更多