优化基于ArrayList的泛型归并排序:避免Object数组与编译警告
泛型归并排序Merge阶段避免Object数组的方案
好问题!在泛型归并排序的merge阶段避开Object数组其实完全可行,核心是利用泛型的类型安全特性或者ArrayList本身的API来替代,不用再靠@SuppressWarnings("unchecked")压警告。下面给你几个实用的方案:
方案1:直接用ArrayList子列表拆分合并
这个方案最直观,完全不用数组,直接借助ArrayList的subList方法拆分左右两个子序列,然后直接比较合并回原列表。代码简洁易读,没有类型转换的麻烦:
public static <T> void merge(ArrayList<T> list, int left, int mid, int right, Comparator<T> comparator) { // 提取左右子列表(subList是视图,创建新ArrayList避免修改原列表时的联动影响) ArrayList<T> leftSub = new ArrayList<>(list.subList(left, mid + 1)); ArrayList<T> rightSub = new ArrayList<>(list.subList(mid + 1, right + 1)); int i = 0, j = 0, k = left; // 合并两个子列表到原列表 while (i < leftSub.size() && j < rightSub.size()) { if (comparator.compare(leftSub.get(i), rightSub.get(j)) <= 0) { list.set(k++, leftSub.get(i++)); } else { list.set(k++, rightSub.get(j++)); } } // 处理左子列表剩余元素 while (i < leftSub.size()) { list.set(k++, leftSub.get(i++)); } // 处理右子列表剩余元素 while (j < rightSub.size()) { list.set(k++, rightSub.get(j++)); } }
优点:代码简洁,无类型警告,无需反射,可读性拉满;
缺点:会创建两个临时ArrayList对象,有轻微的额外内存开销,但绝大多数业务场景下完全可以忽略。
方案2:通过反射创建类型安全的泛型数组
如果你不想创建额外的ArrayList对象,可以用Java反射的Array.newInstance方法直接创建T[]类型的数组,从根源上避免Object数组的转换:
import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Comparator; public static <T> void merge(ArrayList<T> list, int left, int mid, int right, Comparator<T> comparator) { int leftSize = mid - left + 1; int rightSize = right - mid; // 利用反射创建T类型的数组(注意:若list对应位置元素为null,getClass会抛NullPointerException) T[] leftArr = (T[]) Array.newInstance(list.get(left).getClass(), leftSize); T[] rightArr = (T[]) Array.newInstance(list.get(mid + 1).getClass(), rightSize); // 拷贝原列表元素到临时数组 for (int i = 0; i < leftSize; i++) { leftArr[i] = list.get(left + i); } for (int j = 0; j < rightSize; j++) { rightArr[j] = list.get(mid + 1 + j); } // 标准合并逻辑 int i = 0, j = 0, k = left; while (i < leftSize && j < rightSize) { if (comparator.compare(leftArr[i], rightArr[j]) <= 0) { list.set(k++, leftArr[i++]); } else { list.set(k++, rightArr[j++]); } } while (i < leftSize) { list.set(k++, leftArr[i++]); } while (j < rightSize) { list.set(k++, rightArr[j++]); } }
如果担心null元素导致的异常,可以把Class<T>作为参数传入,让方法更安全:
public static <T> void merge(ArrayList<T> list, int left, int mid, int right, Comparator<T> comparator, Class<T> clazz) { int leftSize = mid - left + 1; int rightSize = right - mid; T[] leftArr = (T[]) Array.newInstance(clazz, leftSize); T[] rightArr = (T[]) Array.newInstance(clazz, rightSize); // 后续拷贝和合并逻辑同上 }
优点:无额外ArrayList对象,内存开销更小;
缺点:引入反射,代码复杂度略高,需要处理null元素的边界情况。
方案3:JDK17+用List.copyOf简化代码
如果你的项目用的是JDK17及以上,可以用List.copyOf创建不可变子列表,代码更简洁:
import java.util.ArrayList; import java.util.Comparator; import java.util.List; public static <T> void merge(ArrayList<T> list, int left, int mid, int right, Comparator<T> comparator) { List<T> leftSub = List.copyOf(list.subList(left, mid + 1)); List<T> rightSub = List.copyOf(list.subList(mid + 1, right + 1)); int i = 0, j = 0, k = left; while (i < leftSub.size() && j < rightSub.size()) { list.set(k++, comparator.compare(leftSub.get(i), rightSub.get(j)) <= 0 ? leftSub.get(i++) : rightSub.get(j++)); } while (i < leftSub.size()) list.set(k++, leftSub.get(i++)); while (j < rightSub.size()) list.set(k++, rightSub.get(j++)); }
优点:代码极度简洁,无类型警告,List.copyOf是JDK原生优化的方法,性能不错;
缺点:依赖JDK17+,兼容性有限。
内容的提问来源于stack exchange,提问作者JimBelushi2
相关产品推荐
相关产品推荐

