数据结构前置知识—泛型
一、包装类
1.1什么是包装类
简单来说,包装类型就是基本数据类型的plus版本,也可以说包装类就是引用数据类型,它可以更好的支持面向对象。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1.2装箱和拆箱
装箱指将基本数据类型给包装(引用)类型,反之,拆箱就是将包装(引用)数据类型给基本数据类型。
1.3自动装箱和自动拆箱
自动装箱也称隐式装箱,就是不调用valueOf方法。反之,显示的就是调用valueOf方法,显示装箱的类型根据自己需要改变,如:Byte a = Byte.valueOf();同理显示拆箱也一样,如:Byte a = c.byte()。
public class Test {
//自动装箱
Integer a = 100;
//自动拆箱
int b = a;
//显示装箱
int dd = 15;
Integer ee = Integer.valueOf(dd);
//显示拆箱
Integer cc = 77;
int aa = cc.intValue();
}
面试题
以下代码的输出结果
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a==b);
System.out.println(c==d);
结果:true false
原因
我们知道包装类的底层是调用valueOf方法,那么我们首先应该想到去看这个方法的源代码。
这段代码的意思是,如果i在[low,high]范围内([-128,127]),那么就返回i + (-IntegerCache.low)下标的值,如果不在这个范围,就new一个新对象,即产生一个新地址,返回新地址的值。
例如:i=-128,在范围内,那么就返回下标为-128+(-(-128))的值,即返回下标为0的值,就是128。因此,上述代码中100在范围内,所以返回同样下标的值,就相等,而200不在范围内,两个变量就会分别产生新对象即新地址,值不一样,就不相等。
二、泛型
2.1什么是泛型
泛型简单来说就是一种语法。
2.2引出泛型
思考下面的题
实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值?
思路
2.2.1Object类
既然是存放任意类型的数组,那么我们就定义一个Object类型的数组
class MyArray {
public Object[] array = new Object[10];
public Object getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,Object val) {
this.array[pos] = val;
}
}
public class Test {
public static void main(String[] args) {
//创建一个可以放任意数据类型的数组
//1.Object类数组
MyArray myArray = new MyArray();
myArray.setVal(0,10);
myArray.setVal(1,"hello");//字符串也可以存放
// String ret = myArray.getPos(1);//编译报错 因为向下转型要强转
String ret = (String) myArray.getPos(1);
System.out.println(ret);
}
}
结果:hello
所以这种方法是可行的,当前数组任何数据都可以存放,会显得很杂乱,并且每次要获取都要强转很麻烦,所以我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
2.2.2泛型
语法
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
//可以是任意字母 只是一个占位符 表示是一个泛型类
此时上述代码可以改写为
class MyArray <T>{
//泛型写法
//public [T] array = ([T])new Object[10];
//T[] array = new T[10];这两种写法都是不对的
public Object[] array = new Object[10];
public void setVal(int pos,T val) {
this.array[pos] = val;
}
public T getPos(int pos) {
return (T) this.array[pos];
}
}
public class Test {
public static void main(String[] args) {
//2.泛型
MyArray<Integer> myArray = new MyArray<>();//2
myArray.setVal(0,10);
myArray.setVal(1,12);
int ret = myArray.getPos(1);//3
System.out.println(ret);
MyArray<String> myArray1 = new MyArray<>();
myArray1.setVal(2,"bit");//4
String ret1 = myArray1.getPos(2);
System.out.println(ret1);
}
}
从以上代码可以看出泛型的主要功能就是可以传递类型
T[] array = new T[10];这种写法是因为,泛型概念只是在编译的时候有,当运行到JVM时,就没有泛型概念那么就无法知道这个泛型具体指哪个类型
public [T] array = ([T])new Object[10];
2.2泛型工作机制
在编译时,将T擦除为Object。
三、泛型上界
3.1语法
class 泛型类名称<类型形参 extends 类型边界> {
…
}
3.2举例
//1.泛型上界
//表示可以传Number或是Number子类的数据 即包装类 传String就不行
//class Person{
//
//}
可以传Person和Student类型 因为Student是子类
//class Student extends Person{
//
//}
//class Myarray<T extends Person>{
//
//}
public class Test {
// public static void main1(String[] args) {
// //1.泛型上界
// MyArray <Number> myArray = new MyArray<>();
// MyArray <Integer> myArray1 = new MyArray<>();
// System.out.println("============");
// Myarray <Person> array = new Myarray();
// Myarray <Student> arrays = new Myarray<>();
// }
3.3复杂的例子
比较出数组的最大值
class MyArray<T>{
//表示这个T类型实现了Comparable接口
public Object[] array = new Object[10];
//比较方法
public T FindMax(T array[]){
T max = array[0];
for (int i = 1; i <array.length; i++) {
//因为T类型一定是包装类型 包装类型都实现了Comparable接口
if(max<array[i]){//这里报错
max = array[i];
}
}
return max;
}
}
报错原因:1.首先传入的T一定是引用数据类型,引用数据类型在比较的时候,是实现了Comparable接口,然后调用compareTo方法,但是T在编译时被擦除为Object,而Object没有实现Comparable接口,因此如法进行比较。所以要让T实现这个接口。
2.修改之后,依然报错是因为,实现了接口以后,就可以调用Compare方法进行比较。
class MyArray<T extends Comparable<T>>{
//表示这个T类型实现了Comparable接口
public Object[] array = new Object[10];
//比较方法
public T FindMax(T array[]){
T max = array[0];
for (int i = 1; i <array.length; i++) {
//因为T类型一定是包装类型 包装类型都实现了Comparable接口
if(max.compareTo(array[i])<0){//前面的小于后面的 返回0 更新max
max = array[i];
}
}
return max;
}
}
3.分别传入两个类型,一个Integer类,一个Person类
class MyArray<T extends Comparable<T>>{
//表示这个T类型实现了Comparable接口
//比较方法
public T FindMax(T array[]){
T max = array[0];
for (int i = 1; i <array.length; i++) {
//因为T类型一定是包装类型 包装类型都实现了Comparable接口
if(max.compareTo(array[i])<0){
max = array[i];
}
}
return max;
}
}
public static void main(String[] args) {
//2.举例 比较出数组的最大值
MyArray <Integer> myArray = new MyArray<>();
Integer[] array ={8,15,6,19,77};
System.out.println(myArray.FindMax(array));
System.out.println("============");
MyArray<Person> myArray1 = new MyArray<>();//报错
}
结果:77
**报错原因:<T extends Comparable<T>>这个的意思是,传入的引用类型一定实现了Comparable接口,但是Person没有实现这个接口,要想不报错,就要让Person实现这个接口,并且重写CompareTo方法。**
class Person implements Comparable<Person>{
@Override
public int compareTo(Person o) {
return 0;
}
}
3.4泛型方法
class MyArray{
public T FindMax(T array[]){
T max = array[0];
for (int i = 1; i <array.length; i++) {
//因为T类型一定是包装类型 包装类型都实现了Comparable接口
if(max.compareTo(array[i])<0){
max = array[i];
}
}
return max;
}
}
报错,原因是类是普通类,而方法是泛型方法,在new一个对象时,没有传入指定类型,所以不知道T是什么,因此改为
class MyArray{
public <T extends Comparable<T>>T FindMax(T array[]){
T max = array[0];
for (int i = 1; i <array.length; i++) {
//因为T类型一定是包装类型 包装类型都实现了Comparable接口
if(max.compareTo(array[i])<0){
max = array[i];
}
}
return max;
}
}
public static void main(String[] args) {
//3.泛型方法
MyArray myArray = new MyArray();
Integer[] array ={8,15,6,19,77};
//这里的Integer可传可不传 不传 那么就根据传入的实参类型推导出T的类型 传了 直接确定T的类型
System.out.println(myArray.<Integer>FindMax(array));
}
3.5静态泛型方法
在方法前加static 调用时用类名.直接调用 就不用实例化对象
```
public static void main(String[] args) {
//3.泛型方法
Integer[] array ={8,15,6,19,77};
System.out.println(MyArray.<Integer>FindMax(array));
}
```
ray));
}
### 3.5静态泛型方法
在方法前加static 调用时用类名.直接调用 就不用实例化对象
```
public static void main(String[] args) {
//3.泛型方法
Integer[] array ={8,15,6,19,77};
System.out.println(MyArray.<Integer>FindMax(array));
}
```