【数据结构】深入了解学习数据结构的前置知识

数据结构前置知识

在学习Java数据结构前,我们需要一些前置知识:1.集合,2.泛型, 3.包装类,

这篇文章:主要是认识Java中的集合类、包装类、泛型,以及了解复杂度。

01集合框架

1.什么是集合框架

Java集合框架Java Collection Framework,又被称为容器container,是定义在java.util包下的一组接口interfaces和其实现类classes
其主要表现为将多个元素element置于一个单元中,用于对这些元素进行快使便捷的存储store、检索retrieve、管理manipulate,即平时我们俗称的增删查改CRUD
例如,一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)、一个通讯录一组姓名和电话的映射关系)等等。

其本质就是说:数据结构的种类很多

那什么是数据结构?

数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在任一种或多种特定关系的数据元素的集合。

可以简单理解为:数据+结构-->描述或组织一些数据

下图是类和接口的总览

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJKnUN5Y-1689070951133)(D:\Pictures\未命名文件.png)]

02包装类

1.介绍

包装类(wrapper):在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。

基本数据类型和对应的包装类如下表:

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

其中除了intchar分别对应IntegerCharacter,其他基本数据类型都是首字母大写。

2.装箱和拆箱

所谓的装箱,可以这样理解:

基本类型->包装类型

反之,即为拆箱

下面给出示例代码:

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        Integer b = a;
        System.out.println(b);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3jQEdwQ8-1689070951135)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230711120859258.png)]

我们要了解一个前提:

1.jdk5前是手动装箱和拆箱的。
2.jdk5(含jdk5)是自动装箱和拆箱的。

3.自动装箱和自动拆箱

自动装箱

自动装箱的底层是调用了valueOf方法,何以见得?我们可以以上面的代码,进入其反汇编语言中分析。

在这里插入图片描述

在这里插入图片描述

我们可以在IDEA中清晰的看出,在valueOf(int)中传进来一个参数i ,返回时new 了一个Integer(i)

即:

public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        Integer b = a;//自动装箱
        System.out.println(b);

        Integer c = Integer.valueOf(a);//显示装箱
        Integer d = new Integer(a);//显示装箱
        System.out.println(c);
        System.out.println(d);
    }
}

在这里插入图片描述

自动拆箱

下面给出示例代码:

public static void main(String[] args) {
        Integer a = 10;
        int b = a;
        System.out.println(b);
    }

在这里插入图片描述

public static void main(String[] args) {

        Integer val = new Integer(10);
        int val1 = val;//自动拆箱
        System.out.println(val1);

        //显示拆箱 拆箱为自己指定的元素
        int val2 = val.intValue();
        System.out.println(val2);

        double val4 = val.doubleValue();
        System.out.println(val4);
    }

在这里插入图片描述

在这里插入图片描述

一道经典面试题

请输出下面程序的结果。

public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        System.out.println(a == b);

        Integer a2 = 128;
        Integer b2 = 128;
        System.out.println(a2 == b2);
    }

是不是很多人第一眼就直接是true true,其实不然,这道题的结果是true false

为什么呢?

这其实就涉及到了Integer的取值范围,Integer的取值范围[-128,127]

03初识泛型

1.介绍

在《Java编程思想》中,对泛型的介绍是这样的:一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。

通俗来说:泛型的作用就是在指定的容器中,要持有什么类型的对象,将类型作为参数去传递,需要什么就传什么,让编译器去做检查。

2.引出

这里抛出一个问题:要实现一个类,类中包含一个数组 ,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值,该怎么去实现?

思路
  1. 在数组中我们只能存放指定类型的元素。
  2. 但是我们有顶级父类Object,那么是否可以将创建一个Object数组呢?

说干就干,我们可以尝试一下,示例代码如下:

class MyArray{
    public Object[] array = new Object[10];

    public void set(int pos, Object val) {
        array[pos] = val;
    }

    public Object get(int pos){
        return array[pos];
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.set(0,"123");
        myArray.set(1,90);
    }
}    

这里显然是可以的。

但是这里还是有问题的,如下图

在这里插入图片描述

这里的String str = myArray.get(0);会编译错误,因为get方法的返回值是Object,所以这里是需要强制类型转换的,

String str = (String) myArray.get(0);,这样才 ok,但是这样会很麻烦,那么在这里我们可以引入泛型。

3.语法

泛型的语法如下:

class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1,T2,...,Tn>{
}

class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参*/{
// 这里可以使用类型参数
}
class ClassName<T1,T2,...,Tn>extends Parents<T1> {
// 可以只使用部分类型参数
}

interface 接口<T> {
} 
泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象

那么我们可以将上述的代码进行如下改写:

//<T>:代表当前类是一个泛型类
class MyArray<T>{
    public Object[] array = new Object[10];

    public void set(int pos, T val) {
        array[pos] = val;
    }

    public T get(int pos){
        return (T)array[pos];
    }
}

public class Test1 {
    public static void main(String[] args) {
        MyArray<String> myArray = new MyArray<>();

        myArray.set(0, "java");

        //myArray.set(1, 90);  这里不能放整型了,90会报错

        String str = myArray.get(0);
        System.out.println(str);
        
        MyArray<Integer> myArray2 = new MyArray<>();
        myArray2.set(0,1);
        myArray2.set(1,10);
        
        Integer a = myArray2.get(0);
        System.out.println(a);
    }
}    

4.细节及注意事项

了解

类名后的<T>代表占位符,表示当前类是一个泛型类

【规范】类型形参一般使用一个大写字母表示,常用的名称有:
E表示Element
K表示Key
V表示Value
N表示Number
T表示Type
S,U,V等等-第二、第三、第四个类型
注意点
  1. 不允许实例化一个泛型数组。

在这里插入图片描述

若是非要的话public T[] arr = (T[]new Object[10]);但是这样写也不好。没什么必要。

  1. 泛型只能接受类,所有的基本数据类型必须使用包装类!
  2. <>当中,只能是引用类型,不能是基本类型

在这里插入图片描述

5.了解Raw Type

裸类型是一个泛型类但没有带着类型实参,例如MyArrayList就是一个裸类型
MyArraylist = new MyArray();

在这里插入图片描述

这样子的话,又如同一开始引出泛型时的例子一样了,又得去强制类型转换。

**注意:**我们不要自己去使用裸类型,裸类型是为了兼容老版本的AP保留的机制
下面的类型擦除部分,也会讲到编译器是如何使用裸类型的。
小结:

  1. 泛型是将数据类型参数化,进行传递

  2. 使用<T>表示当前类是一个泛型类。

  3. 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换

6.泛型编译

6.1擦除机制

从反汇编来看,通过命令:javap -c 查看字节码文件,所有的T都是Object

在这里插入图片描述

在编译的过程中,将所有的T替换为Object这种机制我们称为:擦除机制

运行的时候没有泛型这样的概念–>泛型的擦除机制只是存在于编译期间

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

那么这里我们提出问题?

  1. 那为什么,T[] arr = new T[10]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new Object[5]吗?

在这里插入图片描述


这里其实我们要明确:数组在Java里面是一个非常特殊的存在,在JVM里面其实相当于一种数据类型了。

class MyArray<T>{
    public T[] array = (T[])new Object[10];

    public void set(int pos, T val) {
        array[pos] = val;
    }

    public T get(int pos){
        return (T)array[pos];
    }

    public T[] getArray() {
        return array;
    }
}

public class Test1 {
    public static void main(String[] args) {
        //泛型是如何编译的
        MyArray<String> myArray = new MyArray<>();

        myArray.set(0, "java");

        String [] ret = myArray.getArray();
        System.out.println(Arrays.toString(ret));

    }
]    

在这里插入图片描述

类型转换异常

因为整个数组是Object类型数组,意味着这个数组里面什么都能放,那么我们能证明数组里全部类型都是String类型吗?显然不行。

对代码进行改写

class MyArray<T>{
   // public T[] array = (T[])new Object[10];
    public Object[]array = new Object[10];
    public void set(int pos, T val) {
        array[pos] = val;
    }

    public T get(int pos){
        return (T)array[pos];
    }

    public T[] getArray() {
        return (T[])array;
    }
}

public class Test1 {
    public static void main(String[] args) {
        //泛型是如何编译的
        MyArray<String> myArray = new MyArray<>();

        myArray.set(0, "java");

        Object [] ret = myArray.getArray();
        System.out.println(Arrays.toString(ret));

    }
}    

在这里插入图片描述

7.泛型上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

7.1语法
class 泛型类名称<类型形参 extends 类型边界> {
...
}
应用
public class MyArray<E extends Number>{

}

只接受Number的子类型作为E的类型实参

MyArray<Integer>a;//正常,因为Integer是Number的子类型
MyArray<String>b;//编译错误,因为String不是Number的子类型
error:type argument String is not within bounds of type-variable E
  MyArrayList<String>b;
       Λ
where E is a type-variable:
  E extends Number declared in class MyArrayList

了解:没有指定类型边界E,可以视为E extends Object

例题

写一个泛型类 实现一个方法 这个方法球任意类型数组的最大值

在这里插入图片描述

为什么会报错呢?

因为两个Object之间是不能比较大小的。

改进:

//T extends Comparable<T>  T一定是实现Comparable接口的
class Alg<T extends Comparable<T>>{
    public T findMax(T[]array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0){
                max = array[i];
            }
        }
        return max;
    }
}

8.泛型方法

如下示例代码:

class Alg2{
    //泛型方法
    public<T extends  Comparable<T>> T findMax(T[]array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(max) > 0){
                max = array[i];
            }
        }
        return max;
    }
}

public class Test1 {
    public static void main(String[] args) {
        Alg2 alg2 = new Alg2();
        Integer[] array = {1,13,53,232,9};

        Integer ret = alg2.findMax(array);
        System.out.println(ret);
    }
}    

至此,Java版本数据结构的前置知识就大致介绍完了,接下来将会持续更新数据结构相关博文。

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值