动态数组、泛型类型擦除和泛型数组

声明: 本文主要作为作者的复习笔记,由于作者水平有限,难免有错误和不准确之处,欢迎读者批评指正.

目录快捷跳转

    • 基本数组特点
    • 动态数组本质
    • 定义线性表的规范
    • 泛型
    • 泛型类的定义 <类型参数,单个的大写字母代替>
    • 泛型的注意点: 这三个问题的本质就在于泛型只存在编译阶段,运行阶段没有泛型(类型擦除);
    • 类型擦除
    • 正确使用泛型数组的方式
    • 线性表
    • List接口中的常用方法

基本数组特点

保存的单个相同类型的元素,一旦声明一个原始数组,无论采用哪种实例化方式,最终数组一旦定义,长度固定;

动态数组本质

将原始的数组封装到类中,对用户(使用者)淡化数组长度的概念; 当数组长度不够时,类的内部自己进行扩容操作,用户无需关心数组的越界问题;

动态数组 = 基本数组封装到类中 + 对外提供一系列的方便进行增删改查的方法

定义线性表的规范

  • 一个类能称之为是线性表的子类,应该具备类似的行为(都是保存单个元素的集合,集合中元素的类型是相同的,元素之间逻辑上连续);
  • 无论是数组还是链表,都属于线性表的一种,因此只要是线性表的子类,都应该具备基础的CURD方法;
  • 定义线性表接口的好处,就是可以以非常低的成本来更换具体的子类;

泛型

定义阶段不明确具体类型,产生对象时明确具体类型;

泛型和Object最大的区别:

  1. Object转为其他的类型都需要强转,只要强转都有类型转换出错的风险;
  2. 泛型的出现就是为了解决该问题,有泛型不需要进行类型转换,编译阶段就可以校验设置的类型是否是产生对象时的类型(编译阶段类型守门员);

泛型类的定义 <类型参数,单个的大写字母代替>

class Point<T>{
	T t;
}

产生泛型类Point的对象

Point<String> p1 = new Point<>(); //p1就是String类型的Point对象
Point<Integer> p2 = new Point<>(); //p2就是Integer类型的Point对象

泛型方法
权限修饰符 <类型参数> 方法返回值 方法名称(参数列表)

//这里的表示fun是一个泛型方法,T表示返回值为泛型,这里的形参也是泛型
public <T> T fun(T t){
	return t;
}

当泛型类和泛型方法共存时,泛型方法始终以自己的类型参数为准;

泛型的注意点: 这三个问题的本质就在于泛型只存在编译阶段,运行阶段没有泛型(类型擦除);

  1. 泛型只能用在成员域,不能用在静态域,static修饰的内容不能使用泛型;
  2. 产生泛型对象时,具体的类型不能使用基本数据类型,要用基本类型的话统一使用包装类;
  3. 不能直接创建和实例化泛型数组,要使用泛型数组,统一使用Object数组;

类型擦除

泛型信息只存在于编译阶段,在进入JVM之前,与泛型有关的所有信息会被编译器擦除掉,专业术语称为"类型擦除";

  • 通俗来说,也就是泛型类和普通类在进入JVM之后,都一样,就没有泛型类了,就没有泛型这个东西了;
  • 泛型只存在于程序的编译阶段,当javac将源代码编译为class文件之后,与泛型相关的所有信息全都被擦除掉了;
  • 一般的泛型都会擦除为Object类型;
  • 若存在泛型上限,擦除为对应的泛型上限;

正确使用泛型数组的方式

要使用泛型数组,统一定义为Object数组,然后再CURD(增删改查数组时,使用泛型);

private Object[] values = new Object[10];
int size = 0;
//添加元素时保证类型是正确的,后面的查询、修改、删除、强转一定都是正确的
public void add(T t){
	values[size] = t;
	size ++;
}
public T get(int index){
	//此时强转不会报错,add方法时使用泛型来做编译期的类型校验!
	return (T) valus[index];
}

线性表

保存元素的集合是一个具有相同数据类型的集合,元素之间的排列呈直线,因此将这种数据结构称为线性表;

常见的线性表:
数组、链表、栈、队列、字符串(String);

  • 线性表在逻辑上是线性结构,也就是说是连续的一条直线; 但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储;
  • 线性表是一个比较抽象的概念,具体实现有很多; 数组、链表、栈、队列、字符串(String)都是具体的线性表;
  • Java接口优先原则,无论具体是哪种线性表,操作的方法应该对外都是统一的;
  • 接口定义了所有实现子类所共同应当具备的行为和规范;
  • JDK中线性表的接口称为List接口,无论是数组实现还是链表的实现,操作线性表的方法都已经在List接口中定义好了;

满足以下两个要求的数据结构统称为线性表

  1. 保存在这个结构中的元素都是相同的数据类型,不可能一会是int,一会是double,一会又是字符串;
  2. 元素之间线性排列,元素和元素之间逻辑上连续的! 索引为0的元素一定是"逻辑"上在索引为1的元素之前;

List接口中的常用方法

在接口中定义的这些方法,无论哪个具体子类都需要实现; 更换具体子类,使用的方法完全不变;

方法 解释
boolean add(E e) 尾插 e
void add(int index, E element) 将 e 插入到 index 位置
boolean addAll(Collection c) 尾插 c 中的元素
E remove(int index) 删除 index 位置元素
boolean remove(Object o) 删除遇到的第一个 o
E get(int index) 获取下标 index 位置元素
E set(int index, E element) 将下标 index 位置元素设置为 element
void clear() 清空
boolean contains(Object o) 判断 o 是否在线性表中
int indexOf(Object o) 返回第一个 o 所在下标
int lastIndexOf(Object o) 返回最后一个 o 的下标
List< E > subList(int fromIndex, int toIndex) 截取部分list

你可能感兴趣的:(数据结构,Java,java,数据结构,链表)