Hive sql 常見資料傾斜(型別不匹配、複雜join條件)的分析解決
泛型程式設計
寫在最前
泛型 VS 強制型別轉換
使用泛型機制編寫的程式程式碼要比那些雜亂的使用Object變數,然後再進行強制型別轉換的程式碼具有更好的安全性以及可讀性
為什麼要使用泛型程式設計
泛型程式設計意味著編寫的程式碼可以被很多不同型別的物件所重用
定義簡單的泛型類
泛型類就是具有一個或者多個型別變數的類,一般而言,型別變數使用大寫形式並且比較短。
public class Pair<T>{ private T first; private T second; public Pair(){first = null;second = null;} public Pair(T first,T second){this.first = first;this.second = second;} public T getFirst(){return first;} public T getSecond(){return second;} public void setFirst(T newValue){first = newValue;} public void setSecond(T newValue){second = newValue;} }
用具體的型別替換泛型類的型別變數就可以實現例項化泛型類,其實,泛型類可以看作是普通類的工廠
使用泛型類
/** * @return return 的是一個Pair類 */ public static Pair<String> minmax(String[] a){ if(a == null || a.length == 0) return null; String min = a[0]; String max = a[0]; for(int i = 0;i<a.length;i++){ if(min.compareTo(a[i]) > 0) min = a[i]; if(max.compareTo(a[i]) < 0) max = a[i]; } return new Pair<>(min,max); } /** String[] words = {"Mary","had","a","little","lamb"}; Pair<String> mm = ArrayAlg.minmax(words); System.out.println("min = "+mm.getFirst()); System.out.println("max = "+mm.getSecond()); */
泛型方法
泛型方法就是一個帶有型別引數的簡單方法
public static <T> T getMiddle(T... a){ return a[a.length/2]; } /** String middle = ArrayAlg.getMiddle("John","Q.","Public"); // 注意:如果引數是不同種類型的話,是不可以的,例如ArrayAlg.getMiddle(3.14,1729,0)編譯器會報錯 double middle2 = ArrayAlg.getMiddle(3.14,1729.0,0.0); System.out.println(middle); System.out.println(middle2); */
型別變數的限定
有的時候,需要對型別變數加以約束
public static <T extends Comparable> T min(T[] a){
if(a == null || a.length ==0) return null;
T smallest = a[0];
for(int i = 1;i<a.length;i++) if(smallest.compareTo(a[i]) > 0 ) smallest = a[i];
return smallest;
}
讓T extends Comparable介面是為了確保泛型型別T所屬的類有Comparable介面,方法為將泛型型別T限制為實現了Comparable介面的類。為泛型型別T設定限定bound
Comparable是一個介面,為什麼用extend而不用implements,<T extends Comparable & Serializable> 表示T應該是繫結型別的子型別,通過&實現對泛型型別T的多個限定
泛型程式碼和虛擬機器
寫在最前:JAVA虛擬機器(JVM)沒有泛型型別物件,所有的物件都屬於普通類
- 虛擬機器中沒有泛型,只有普通的類和方法
- 所有的型別引數都用他們的限定型別來進行替換
- 橋方法被合稱來保持多型
- 為了保持型別安全性,必要時插入強制型別轉換
型別擦除:無論何時定義一個泛型型別,都自動提供了一個相應的原始型別(raw type),原始型別的名字就是刪除型別引數後的泛型型別名,擦出型別變數,並替換成為限定型別(無限定型別用Object)
例如,上述Pair
的原始型別如下(因為T是一個無限定的型別變數,所以直接使用Object替換)
public class Pair{
private Object first;
private Object second;
public Pair(){first = null;second = null;}
public Pair(Object first, Object second){this.first = first;this.second = second;}
public Object getFirst(){return first;}
public Object getSecond(){return second;}
public void setFirst(Object newValue){first = newValue;}
public void setSecond(Object newValue){second = newValue;}
}
再來一個原始型別類的轉換
public class Interval<T extends Comparable & Serializable> implements Serializable{
private T lower;
private T upper;
...
public Interval(T first,T second){
if(first.compareTo(second) <=0){
lower = first;
upper = second;
}else{
lower = second;
upper = first;
}
}
}
原始型別Interval如下所示
public class Interval implements Serializable{
private Comparable lower;
private Comparable upper;
...
public Interval(Comparable first, Comparable second){...}
}
如果是class Interval<T extends Serializable & Comparable> 那麼原始型別會用Serializable替換T
翻譯泛型表示式:當程式呼叫泛型方法時,如果擦除返回型別,編譯器插入強制型別轉換
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
JVM對原始方法Pair.getFirst()方法的呼叫,然後將返回的Object類強制轉換成為Employee型別
翻譯泛型方法
型別擦除也會出現在泛型方法中
public static <T extends Comparable> T min(T[] a){}
擦除型別之後
public static Comparable min(Comparable[] a){}
有時候,型別擦除會帶來複雜的問題,如下例項:
class DateInterval extends Pair<LocalDate>
{
public void setSecond(LocalDate second){....}
}
// 型別擦除之後
class DateInterval extends Pair
{
public void setSecond(LocalDate second){....}
}
// 存在另一個從Pair繼承的setSecond方法
public void setSecond(Object second){...}
上述方法擦除就產生了問題,問題在於型別擦除與多型發生了衝突,想要解決這個問題,就需要在編譯器在DateInterval類中生成一個橋方法(bridge method)
class DateInterval extends Pair<Localdate>{
public void setSecond(Object second){setSecond((Date) second);}
}