阿里Java开发手册剖析:
-
6.5【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException
-
6.8【强制】在subList场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException
-
6.9【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。
-
6.11【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法, 它的 add/remove
-
6.14【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式
-
6.16【推荐】集合泛型定义时,在 JDK7 及以上,使用 diamond 语法或全省略。 说明:菱形泛型,即 diamond,直接使用<>来指代前边已经指定的类型。
【强制】在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort, Collections.sort 会抛 IllegalArgumentException 异常。
说明:三个条件如下
- x,y 的比较结果和 y,x 的比较结果相反。
- x>y,y>z,则 x>z。
- x=y,则 x,z 比较结果和 y,z 比较结果相同。
其实主要漏洞是,我们在比较大小时,忘记了处理相等的情况。比如下面这种写法:
@Override
public int compareTo(Person o) {
return this.age > o.age? 1 : -1;
}
如何过传入一个age相同的Person对象,会返回-1;所以他会排到前面的位置,认为是小的对象。 那么为啥JDK7之后才需要这么注意呢?
因为JDK7之后sort由
TimeSort
来实现,里面采用了一套新的排序算法。这套算法就需要处理好相等的额情况。
java.lang.ClassCastException: xxxxx cannot be cast to java.lang.Comparable
我们可以通过Arrays.sort, Collections.sort
对数组和集合进行排序。
但是这个对象必须是实现了Comparable<T>
接口。
看下面这个反面例子:
public class sort {
public static void main(String[] args) {
Person[] persons = {
new Person(10, 1, "yang"),
new Person(11, 1, "ge"),
new Person(12, 0, "peng"),
};
Arrays.sort(persons);
for (Person person : persons) {
System.out.println(person.toString());
}
}
}
class Person {
int age;
int sex;
String name;
public Person(int age, int sex, String name) {
this.age = age;
this.sex = sex;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", sex=" + sex +
", name='" + name + '\'' +
'}';
}
}
日志输出:
根据日志提示可以看到。Arrays.sort(Object[] a)
是通过ComparableTimSort
。追踪源码可见:
Arrays.sort-->
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
ComparableTimSort-->
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {
assert a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi);
binarySort(a, lo, hi, lo + initRunLen);
return;
}
...
}
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
...
if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
countRunAndMakeAscending(Object[] a, int lo, int hi)
会对传入的数组元素强转为Comparable
:if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0)
。
所以传入的sort的对象元素必须要实现Comparable<T>
接口。
将上面的Person类修改如下再运行就没问题了:
class Person implements Comparable<Person> {
int age;
int sex;
String name;
public Person(int age, int sex, String name) {
this.age = age;
this.sex = sex;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", sex=" + sex +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Person o) {
return this.age -o.age ;
}
}
日志输出如下:
Person{age=10, sex=1, name='yang'}
Person{age=11, sex=1, name='ge'}
Person{age=12, sex=0, name='peng'}
Process finished with exit code 0
可以看到是按照年级从小到大排序。如果我们修改下public int compareTo(Person o)
内实现:
@Override
public int compareTo(Person o) {
return o.age - this.age;
}
日志输出:
Person{age=12, sex=0, name='peng'}
Person{age=11, sex=1, name='ge'}
Person{age=10, sex=1, name='yang'}
Process finished with exit code 0
就看到按照年级从大到小排。
所以:
public int compareTo(Person o)
的返回值决定了排序的顺序。
public interface Comparable<T> {
/ *
* @param o 比较的对象
* @return 返回一个负数、0、正数,分别代表着小于、等于、大于指定比较对象
*
*/
public int compareTo(T o);
}