首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 排序完全指南:Arrays.sort、Collections.sort、Comparator、Lambda 與並行排序效能對比

     編輯:Java 相關知識

前言

排序是基礎演算法,Java 提供 `Arrays.sort`(陣列)、`Collections.sort`(List)、`Stream.sorted`(流式)三大入口,底層統一使用 **TimSort(合併排序 + 插入排序混合)**。本文系統闡述 API 用法、比較器策略、效能特徵與避坑指南。

一、陣列排序:Arrays.sort

1.1 基本型別陣列(原地排序)


int[] arr = {5, 2, 8, 1, 9, 3};
Arrays.sort(arr); // 升序:[1, 2, 3, 5, 8, 9]
// 基本型別無降序,需自行反轉或轉物件陣列

1.2 物件陣列(自然序/比較器)


String[] strs = {"banana", "apple", "cherry"};
Arrays.sort(strs); // 自然序:按 Comparable 實現

// 自定義比較器
Arrays.sort(strs, (a, b) -> b.length() - a.length()); // 長度降序
Arrays.sort(strs, Comparator.comparing(String::length).reversed()); // 同前

1.3 區間排序


Arrays.sort(arr, 1, 4); // 排索引 1 到 3(含頭不含尾)
Arrays.sort(strs, 0, 2, Comparator.reverseOrder());

1.4 並行排序(Java 8+)


Arrays.parallelSort(arr); // 多核並行,大陣列(>10000)顯著加速
Arrays.parallelSort(strs, Comparator.naturalOrder());

二、List 排序:Collections.sort vs List.sort

2.1 Collections.sort(傳統)


List list = new ArrayList<>(Arrays.asList("banana", "apple", "cherry"));
Collections.sort(list); // 自然序,修改原列表
Collections.sort(list, Comparator.reverseOrder()); // 降序

2.2 List.sort(Java 8+ 推薦)


list.sort(Comparator.naturalOrder()); // 介面預設方法,更直觀
list.sort(Comparator.comparing(String::length).thenComparing(String::compareTo));

2.3 不可變排序(產生新列表)


List sorted = list.stream().sorted().collect(Collectors.toList());
// 或
List sorted = new ArrayList<>(list);
sorted.sort(Comparator.naturalOrder());

三、Comparator 策略大全

3.1 基礎比較器


Comparator natural = Comparator.naturalOrder();
Comparator reverse = Comparator.reverseOrder();
Comparator nullsFirst = Comparator.nullsFirst(natural);
Comparator nullsLast = Comparator.nullsLast(natural);

3.2 鍵提取器鏈式組合(核心技巧)


class User { String name; int age; int score; }

// 單鍵
Comparator byAge = Comparator.comparing(User::getAge);
Comparator byName = Comparator.comparing(User::getName, String.CASE_INSENSITIVE_ORDER);

// 多鍵:年齡升序 → 分數降序 → 姓名升序
Comparator multi = Comparator
    .comparing(User::getAge)
    .thenComparing(Comparator.comparing(User::getScore).reversed())
    .thenComparing(User::getName);

// 處理 null
Comparator safe = Comparator
    .comparing(User::getName, Comparator.nullsFirst(String::compareTo));

3.3 基本型別專用比較器(避免裝箱)


Comparator byAge = Comparator.comparingInt(User::getAge);
Comparator byScore = Comparator.comparingDouble(User::getScore);
Comparator byLong = Comparator.comparingLong(User::getId);

3.4 比較器組合數學

  • `thenComparing`:前鍵相等時比較後鍵
  • `thenComparingInt/Long/Double`:基本型別版
  • `reversed()`:反轉整個比較器鏈

四、Stream 排序


List users = ...;

// 自然序
List sorted = users.stream().sorted().collect(Collectors.toList());

// 比較器
List sorted = users.stream()
    .sorted(Comparator.comparing(User::getAge).reversed())
    .collect(Collectors.toList());

// 並行排序(大數據量)
List sorted = users.parallelStream()
    .sorted(Comparator.comparing(User::getScore))
    .collect(Collectors.toList());

五、效能基準測試(JMH)


@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class SortBenchmark {
    @Param({"1000", "10000", "100000", "1000000"})
    int size;
    int[] arr;
    Integer[] objArr;
    List list;

    @Setup
    public void setup() {
        arr = new int[size];
        objArr = new Integer[size];
        list = new ArrayList<>(size);
        Random r = new Random(42);
        for (int i = 0; i < size; i++) {
            int v = r.nextInt(size);
            arr[i] = v;
            objArr[i] = v;
            list.add(v);
        }
    }

    @Benchmark public void arraysPrimitive() { Arrays.sort(arr.clone()); }
    @Benchmark public void arraysObject() { Arrays.sort(objArr.clone()); }
    @Benchmark public void arraysParallel() { Arrays.parallelSort(arr.clone()); }
    @Benchmark public void collectionsSort() { Collections.sort(new ArrayList<>(list)); }
    @Benchmark public void listSort() { new ArrayList<>(list).sort(null); }
    @Benchmark public void streamSort() { list.stream().sorted().collect(Collectors.toList()); }
    @Benchmark public void parallelStreamSort() { list.parallelStream().sorted().collect(Collectors.toList()); }

    /* 典型結果(毫秒):
    size=1K    arraysP: 0.2  arraysO: 0.5  parallel: 0.3  coll: 0.6  list: 0.6  stream: 1.2  pStream: 1.5
    size=10K   arraysP: 2.5  arraysO: 6    parallel: 1.8  coll: 7    list: 7    stream: 14   pStream: 8
    size=100K  arraysP: 30   arraysO: 70   parallel: 18   coll: 85   list: 85   stream: 170  pStream: 60
    size=1M    arraysP: 350  arraysO: 850  parallel: 180  coll: 1000 list: 1000 stream: 2000 pStream: 550
    */
}

關鍵結論

  1. 基本型別陣列 `Arrays.sort` 最快(無裝箱、緩存友好、TimSort 高度優化)
  2. 並行排序大陣列(>10K)收益明顯,小陣列開銷大於收益
  3. 物件排序開銷大:裝箱、間接引用、比較器調用
  4. `List.sort` ≈ `Collections.sort`**,前者語義更清晰
  5. Stream 額外開銷大,除非需鏈式操作/並行,優先用集合/陣列原地排序

六、避坑指南

  1. 基本型別無降序**:`Arrays.sort(int[])` 僅升序,需轉 `Integer[]` 或自行反轉
  2. 比較器違反傳遞性**:導致 `IllegalArgumentException: Comparison method violates its general contract`
  3. null 元素未處理**:`NullPointerException`,用 `nullsFirst/Last`
  4. 排序後原陣列/列表被修改**:需保留副本請先 `clone()`/`new ArrayList<>()`
  5. 浮點數比較**:`Double.compare`/`Float.compare` 處理 NaN、-0.0
  6. 大量資料排序**:考慮資料庫 `ORDER BY`、外部排序、分布式排序

總結

**基本型別用 `Arrays.sort/parallelSort`,物件用 `List.sort(Comparator)`,多鍵用 `comparing().thenComparing()`,大數據並行,小數據串行**。掌握 TimSort 特性(部分有序極快、穩定排序)、比較器鏈式組合、基本型別專用比較器避免裝箱,才能寫出高效、正確、優雅的排序代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved