Java数据结构开篇——前置知识


前言

之前花了大概三个月的时间学完了Javase,数据结构也快学完了但是今天才开始总结(懒瘾犯了~ ) 再开始总结之前先讲一些关于数据结构的基础知识,相比于c++、C语言的数据结构,Java与之大同小异,但还是有挺多区别的,同是都是基于线性表、二叉树、栈和队列、优先级队列(堆)等等之类的数据结构,不同的地方在于实现方式,这是由java的基础语法和特性决定的。本篇先做一个总结和综述以及一些前置知识,后面再慢慢写~


一、集合框架

首先需要了解的就是Java的集合框架,也被称为容器,是定义再java.util包下的一组接口interfaces和其实现类classes。

其主要表现为将多个元素置于一个单元中,用于对这些元素进行快速、便捷的存储、检索、管理,即平时我们用到的增删改查。

先看看容器中类和接口的总览

在这里插入图片描述
Iterable:迭代器,相当于for - each(),实现了该接口的类就具备了可迭代的能力
Collection:用来存储管理一组对象(elements)所有的Collection都是可以Iterable的,从继承关系也可以看出来。
Collection并没有规定元素是怎么组织起来的,具体是实现其子接口的子类实现的。
Set : 元素不能重复,背后隐含着查找/搜索的语义。
SortedSet : 一组有序的不能重复的元素。
List: 线性结构。
Queue : 队列。
Deque : 双端队列。
Map : 键值对 Key-Value-Pair ,背后隐含着查找/搜索的语义。
SortedMap : 一组有序的键值对。

上述这些接口下的具体实现类就具备这些接口的特性。

上图中明确了各种数据结构接口和类之间的继承与实现关系,很重要需要烂熟于心的。

二、容器涉及的数据结构和算法

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

1.各种数据结构以及对应的容器介绍

以下容器都是对某种特定数据结构的封装:

  1. Collection:是一个接口,包含了大部分容器常用的一些方法
  2. List:是一个接口,规范了ArrayList 和 LinkedList中要实现的方法
    ArrayList:实现了List接口,底层为动态类型顺序表
    LinkedList:实现了List接口,底层为双向链表
  3. Stack:底层是栈,栈是一种特殊的顺序表
  4. Queue:底层是队列,队列是一种特殊的顺序表
  5. Deque:双端队列,可以实现队列也可以实现栈
  6. Set:集合,是一个接口,里面放置的是K模型
    HashSet:底层为哈希桶,查询的时间复杂度为O(1)
    TreeSet:底层为红黑树,查询的时间复杂度为O(log2N),关于key有序的
  7. Map:映射,里面存储的是K-V模型的键值对
    HashMap:底层为哈希桶,查询时间复杂度为O(1)
    TreeMap:底层为红黑树,查询的时间复杂度为O( log2N),关于key有序

2.什么是算法

算法,听起来很高大上,但是解释起来就是定义良好的计算过程,取一个或一组的值为输入,并产生一个或一组值做为输出,简单来说算法就是一系列的计算步骤,用输入数据的到输出结果。衡量一个算法的好坏就是看计算步骤够不够高效,计算结果够不够准确。

三、时间复杂度和空间复杂度

上面说了,衡量算法好坏的关键是看高不高效,结果准不准确,用术语说就是看其算法效率:分为两种第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间。

1、时间复杂度

定义:算法的时间复杂度是一个数学函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是这很麻烦,而且每个机器的配置不同性能也不同,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度

在实际中一般情况关注的是算法的最坏运行情况

2、空间复杂度

定义:空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也使用大O渐进表示法。

注意:当涉及到递归时,其空间复杂度通常是大于O(1)的,因为要开辟很多栈帧出来。

具体的栗子网上比比皆是,我就不写了~

四、泛型介绍

1、什么是泛型

一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,那这种刻板的限制就需要我们重写代码。

泛型是JDK1.5引入的新语法,通俗的说就是**泛型适用于多种类型,对参数实现了参数泛化。**提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2、泛型的语法特征

java泛型的语法:

class 泛型类名称 <类型形参列表>{

}
class name <T1,T2,T3...>{

}

举个例子就懂了:

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

程序运行起来报错:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。

我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在编译阶段,编译器就会报错

注意:当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写,即写成List arrayList = new ArrayList<>();也可以。

泛型只能只能接受类,且必须是引用数据类型,因此所有的基本数据类型必须写成包装类!

3、泛型特性

泛型只再编译阶段有效,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

4、泛型的使用

泛型有三种使用方式:泛型类、泛型接口、泛型方法

4.1 泛型类

public class ClassDemo <T>{
    private T key;

    //构造方法

    public ClassDemo(T key) {
        this.key = key;
    }

    //这里并不是泛型方法,只是一个普通的成员方法
    public T getKey(){
        return key;
    }

    public static void main(String[] args) {
        ClassDemo<Integer> integerClassDemo=new ClassDemo<>(123456);


        ClassDemo<String> stringClassDemo=new ClassDemo<String>("hellojava");

        System.out.println(integerClassDemo.getKey());

        System.out.println(stringClassDemo.getKey());
    }
}

在这里插入图片描述

事实上,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。

在这里插入图片描述
当我们利用泛型传入类型参数加入限制时,编译器会帮我们纠错,规定了这里需要传入一个Integer类型,我们传入String类型肯定是不行的。

4.2 泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一下List的源码:

在这里插入图片描述在这里插入图片描述

这里的List接口就使用了泛型,使得其可以限制实现该接口的类中元素类型。
看代码:

public class InterfaceDemo {

    //这里使用实现List接口的ArrayList类实例化一个array
    public List<Integer> array=new ArrayList<>();

    public InterfaceDemo() {

    }

    public static void main(String[] args) {
        InterfaceDemo test=new InterfaceDemo();
        test.array.add(1);
        test.array.add(2);
        System.out.println(test.array.get(0));

    }
}

在这里插入图片描述
由于List的类型参数,传入Integer参数就使得这个array的各个操作必须传入Integer类型的数据。当我们将类型参数改为String时,此时就会报错:

在这里插入图片描述

泛型方法本人也没有搞很懂,不懂的的友友们去别的博主那儿去看看吧,我就不误人子弟了hhh~

此外,泛型相关知识点还有很多,诸如通配符、通配符的上下界、泛型的上界、裸类型等等,但是把个人觉得常用的需要了解的都写了。感兴趣的话去这篇博客瞧瞧:

java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

总结

以上就是今天要讲的内容,本文仅仅简单介绍了学习数据结构前需要了解的前置知识,要了解的还有comparable以及comparator以及 自动拆箱、自动装箱、Object的equals方法等等,这些在之前的博客都有写过,这里就不赘述了,链接附在下面:
1、comparable以及comparator 2、Object类和equals方法 3、包装类以及拆箱装箱

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭彭彭摆鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值