java 泛型回忆录
关于JAVA泛型的学习小结:
之 前看C++的模版,里面使用的模版类型和实例化的模版类型理解起来还是比较简单地,就是一个“替代”过程,理解起来是比较顺畅地模版类型和一个具体的类型 在模版内的使用并没有多大的区别。但是JAVA1.5的泛型(GJ)它在使用上和模版是不大相同地,就通过我的笔记大家来好好复习下。(端午节无聊看 地,T _T)
1 类的泛型。就是在类的基础上对未知类型的一种约束。使用起来和模版不同的地方主要有: A 这种约束不会使用在static的类型上。
B 不能在基本类型实例化泛型参数。
C 不能在new,instanceof,数据转换中使用中外露类型参数。
D 不能在类定义的implements和extends里外露类型参数。
AB类型的使用会发出编译器的错误通知。CD类型错误只会发出warning,使用时候有时也可以正常使用,但是这个涉及之后的GJ的泛型实现包括检查和类型擦除。
2 泛型方法(多态方法)
<方法泛型参数> Public 描述符 返回值 方法名(参数)
注意点:多态方法在GJ中是会进行类型推断的。
例子: public static <T> T make_dn() { return new DDN<T>();}
String ts=make_dn();
如上只要指定一次类型参数,GJ会自动推测出是需要DDN<String>的上下文。
3 关于通配符类型。对于Vector<?>,Vector,Vector<T>三种类型的区别。
Vector<?>表示如果有X是Vector<?>类型,那么它可能是Vector<T>里其中的一个。
Vector这个是一般的容器里面都是Object类型没有类型检查。Vector<T>表示对一个未知类型T的约束。 如果我们要操作一种泛型类那么就有必要使用?通配符了。常见的是在一些反射方法中方法参数是Class<?>类型。
4 如果有一个泛型类定义D<T> 那么D<Integer>和D<Object>之间没有关系,不能替换赋值。然而在使用数组时我们可以把一个Integer的数组赋值给Object的数组。
5 关于类型检查和类型擦除。这个是GJ和模版的本质差别。GJ里的泛型参数只用在编译时侯的检查,提供对未知类型的一种约束。在运行时它的字节码和使用没有 泛型代码的JAVA是一样地。那么这里就有一个在编译时对它的泛型参数进行擦除和替换的一个过程了。具体的标准如下:
对于参数化类型,需要删除其中的类型参数,例如,LinkedList<A> 将被"擦除"为 LinkedList;对于非参数化类型,不作擦除,或者说用它自己来擦除自己,例如 String 将被"擦除"为 String;对于类型变量(有关类型变量的说明请参考"泛型概览"相关内容),要用它们的上限来对它们进行替换。 除此之外,还需要注意的一点是,在某些情况下,擦除技术需要引入类型转换(cast),这些情况主要包括:
情况 1. 方法的返回类型是类型参数;
情况 2. 在访问数据域时,域的类型是一个类型参数。
在最后我们看一个具体的泛型类擦除类型后的结果:
泛型类:class Collections {
public static <A implements Comparable<A>>
A max (Collection<A> xs) {
Iterator<A> xi = xs.iterator();
A w = xi.next();
while (xi.hasNext()) {
A x = xi.next();
if (w.compareTo(x) < 0) w = x; }
return w;
}
}
转化后的类:具体使用了类型擦除和上限替代
class Collections {
public static Comparable max(Collection xs){
Iterator xi = xs.iterator();
Comparable w = (Comparable)xi.next();
while (xi.hasNext()) {
Comparable x = (Comparable)xi.next();
if (w.compareTo(x) < 0) w = x;
}
return w;
}
}
在转换的过程中,如果一个类实现了一个参数话的接口或者继承了一个参数化的类,那么为了保证重载的发生,就要引入桥方法。
例子:interface Comparable<A> {
public int compareTo(A that);
}
class Byte implements Comparable<Byte> {
private byte value;
public int compareTo(Byte that) {
return this.value - that.value;
}
}
转化后的类:
interface Comparable {
public int compareTo(Object that);
}
class Byte implements Comparable {
private byte value;
public Byte(byte value) {this.value = value;}
public byte byteValue(){return value;}
//转化后的2个方法应用了重载。底下的就是桥方法。
public int compareTo(Byte that) {
return this.value - that.value;
}
public int compareTo(Object that){
return this.compareTo((Byte)that);
}
}
6 如果要进一步针对泛型进行反射的话。可以参照http://www.ibm.com/developerworks/cn/java/j-cwt11085.html进行更好地研究。写的很好这文章哈哈。
在Arrays中关于基本类型如int,long,float等都在java类库中Arrays的排序算法探析(基础类型)做了一定分析,本篇主要关于Object类型的sort,以及之后的泛型sort。
直接查看源码中的方法定义及实现:
- public static void sort(Object[] a) {
- Object[] aux = (Object[])a.clone();
- mergeSort(aux, a, 0, a.length, 0);
- }
- public static void sort(Object[] a, int fromIndex, int toIndex) {
- rangeCheck(a.length, fromIndex, toIndex);//验证参数是否合法
- Object[] aux = copyOfRange(a, fromIndex, toIndex);//创建待排序数组临时存储区
- mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
- }
由上可见,两个重载方法的具体实现指向了同一个具体实现,应该是个私有方法:
- /**
- * Src is the source array that starts at index 0
- * Dest is the (possibly larger) array destination with a possible offset
- * low is the index in dest to start sorting
- * high is the end index in dest to end sorting
- * off is the offset to generate corresponding low, high in src
- */
- private static void mergeSort(Object[] src,
- Object[] dest,
- int low,
- int high,
- int off) {
- int length = high - low;
- // Insertion sort on smallest arrays
- if (length < INSERTIONSORT_THRESHOLD) {
- for (int i=low; i<high; i++)
- for (int j=i; j>low &&
- ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
- swap(dest, j, j-1);
- return;
- }
- // Recursively sort halves of dest into src
- int destLow = low;
- int destHigh = high;
- low += off;
- high += off;
- int mid = (low + high) >>> 1;
- mergeSort(dest, src, low, mid, -off);
- mergeSort(dest, src, mid, high, -off);
- // If list is already sorted, just copy from src to dest. This is an
- // optimization that results in faster sorts for nearly ordered lists.
- if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
- System.arraycopy(src, low, dest, destLow, length);
- return;
- }
- // Merge sorted halves (now in src) into dest
- for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
- if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
- dest[i] = src[p++];
- else
- dest[i] = src[q++];
- }
- }
当然,如果能直接看代码一眼就完全明白的话,那到此就可以为止了。
先理一下流程:
1 如果待排序的数组大小小于设置的插入排序阈值,则直接采用插入排序解决;
2 如果不满足1,则将其一分为二,递归处理;这样经过多次递归,其实最终还是分段采用插入排序将其拆分为了N个有序的段;
3 到了这里,前面是多个分别有序的段,这里需要将其逐一连接起来,使其更大长度上有序,这一部分展示的就是:如果前一段的最后一个值(也就是前一段的最大值)小于紧挨着的后一个段的第一个值(也就是这一段的最小值),则这两段总体是有序的;
4 如果没有3那种理想状态,则需要我们来处理,这里处理的思想就是:前后两段逐一开始比较,小的先放,依次放第二小的,直到两段都为空;
5 3或4逐级返回,最终构成一个整体的有序数组。
再说泛型的排序:
- public static <T> void sort(T[] a, Comparator<? super T> c) {
- T[] aux = (T[])a.clone();
- if (c==null)
- mergeSort(aux, a, 0, a.length, 0);
- else
- mergeSort(aux, a, 0, a.length, 0, c);
- }
- ublic static <T> void sort(T[] a, int fromIndex, int toIndex,
- Comparator<? super T> c) {
- rangeCheck(a.length, fromIndex, toIndex);
- T[] aux = (T[])copyOfRange(a, fromIndex, toIndex);
- if (c==null)
- mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
- else
- mergeSort(aux, a, fromIndex, toIndex, -fromIndex, c);
- }
可以看出来,泛型排序的处理过程几乎和对象数组的一模一样,值得注意的是,如果Comparator为null,则使用默认的Comparable接口的自然序。下面看具体调用的实现方法:
- private static void mergeSort(Object[] src,
- Object[] dest,
- int low, int high, int off,
- Comparator c) {
- int length = high - low;
- // Insertion sort on smallest arrays
- if (length < INSERTIONSORT_THRESHOLD) {
- for (int i=low; i<high; i++)
- for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
- swap(dest, j, j-1);
- return;
- }
- // Recursively sort halves of dest into src
- int destLow = low;
- int destHigh = high;
- low += off;
- high += off;
- int mid = (low + high) >>> 1;
- mergeSort(dest, src, low, mid, -off, c);
- mergeSort(dest, src, mid, high, -off, c);
- // If list is already sorted, just copy from src to dest. This is an
- // optimization that results in faster sorts for nearly ordered lists.
- if (c.compare(src[mid-1], src[mid]) <= 0) {
- System.arraycopy(src, low, dest, destLow, length);
- return;
- }
- // Merge sorted halves (now in src) into dest
- for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
- if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
- dest[i] = src[p++];
- else
- dest[i] = src[q++];
- }
- }
仔细看来,无Comparator的实现和对象数组的事项没有任何差别,具有Comparator的也仅仅在比较使采取的是compare方法来决定数组顺序。
这个实现值得学习的有几点:
1 小规模的比较采取的是插入排序,这就避免了递归到一个数组只有一个元素那种情况,在小规模情况下,插入排序实现简单,效率也还可以(类库中设置的是7);
2 在相邻两段的连接时,如果发现两个段的连接处构成正确顺序,则直接返回,减少了需要逐个比较元素重新插入一遍带来的性能影响;
3 无符号位运算取中间值,代替/;
4 对外接口与对内实现的独立,对外可能有多个接口,但内部实现其实是一样的,这样非常方便于后续调整和性能优化;
总体感觉就是这些方法的实现极大的优化了性能,并且给后续调整留下了空间,便于维护。
唯一感到有点不足的就是:
就是临时变量实在是有点多,刚开始难以理解~