《編寫高質量代碼:改善 Java 程序的 151 個建議》第 5 章聚焦數組與集合。建議 70-74 涵蓋 subList 視圖機制、局部列表高效處理、併發修改陷阱、Comparator 與 Comparable 排序策略選擇、binarySearch 適用場景五大核心知識點。本文結合 JDK 源碼與實戰案例深度解析。
List.subList(from, to) 返回的不是新列表,而是原列表的動態視圖。視圖上的任何修改(add、remove、set)直接映射到原列表,且視圖大小隨原列表變化而變化。
AbstractList.subList() 返回內部類 SubList(或 RandomAccessSubList),其核心字段:
private final AbstractList<E> l; // 原始列表引用 private final int offset; // 起始偏移量 private int size; // 視圖大小
關鍵方法實現:
public E get(int index) { return l.get(index + offset); }
public void add(int index, E element) { l.add(index + offset, element); /* 更新 size、modCount */ }
public E remove(int index) { E result = l.remove(index + offset); /* 更新 size、modCount */ return result; }
所有操作委託給原列表 l,故視圖與原列表數據始終一致。
String.substring() 創建新字符串(或共享 char[] 但不可變),修改不影響原字符串;subList 則是可變視圖,雙向同步。這是因為 List 設計為可變集合,視圖模式避免數據拷貝開銷。
傳統循環刪除易出錯(索引偏移),且代碼冗長。利用視圖特性,一行代碼搞定:
list.subList(20, 30).clear();
clear() 內部調用 removeRange(0, size()),最終委託原列表批量刪除,效率高且線程安全(單線程下)。
SubList 構造時記錄 expectedModCount = l.modCount。每次視圖操作前調用 checkForComodification() 比對:
if (modCount != l.modCount) throw new ConcurrentModificationException();
若生成視圖後修改原列表,l.modCount 增加,導致視圖後續操作(甚至 size())拋出 CME。此機制為快速失效,非真正併發檢測。
list = Collections.unmodifiableList(list); 生成視圖後凍結原列表。new ArrayList<>(list.subList(from, to)) 產生獨立快照。| 維度 | Comparable (內部比較器) | Comparator (外部比較器) |
|---|---|---|
| 定義位置 | 實體類內部 compareTo |
獨立類實現 compare |
| 數量限制 | 一個類僅有一種自然序 | 可定義無限種排序策略 |
| 耦合度 | 侵入式,修改實體類 | 非侵入式,符合開閉原則 |
| 適用場景 | 默認/通用排序(如 ID、時間) | 業務多維度排序(按部門、薪資、績效) |
先按職位降序,職位相同再按工號升序:
// Employee 實現 Comparable(自然序:按 ID 升序)
@Override public int compareTo(Employee o) {
return CompareToBuilder.reflectionCompare(this, o, "id");
}
// 外部比較器:職位降序 + ID 升序
class PositionComparator implements Comparator<Employee> {
@Override public int compare(Employee o1, Employee o2) {
int posCmp = o2.getPosition().compareTo(o1.getPosition()); // 降序
return (posCmp != 0) ? posCmp : Integer.compare(o1.getId(), o2.getId());
}
}
// 使用
Collections.sort(list, new PositionComparator());
// 或臨時倒序
Collections.sort(list, Collections.reverseOrder(new PositionComparator()));
CompareToBuilder 鏈式調用自動處理空值、多字段排序,代碼更簡潔健壯。
Collections.binarySearch 基於二分查找,要求列表按自然順序升序排列(或提供一致的 Comparator)。無序列表調用結果不可預期。
List<String> cities = Arrays.asList("上海", "廣州", "廣州", "北京", "天津");
// 無序調用
Collections.binarySearch(cities, "廣州"); // 返回 2(中間元素),而非首次出現的 1
// 排序後調用
Collections.sort(cities); // [北京, 廣州, 廣州, 上海, 天津]
Collections.binarySearch(cities, "廣州"); // 返回 1 或 2(任一匹配位置)
indexOf 或 Stream API;若可接受排序副本,new ArrayList<>(list).sort(...) 後再二分查找。建議 70-74 揭示了 Java 集合框架的核心設計哲學:視圖而非拷貝(subList)、策略模式解耦排序邏輯(Comparator)、算法前提條件不可忽視(binarySearch 需有序)。實戰中請牢記:生成視圖後凍結原列表、優先用外部比較器擴展排序、無序查找誠用 indexOf。