数据结构
1.1 数据结构有什么用?
在我们学习集合之前要思考一个问题,为什么要使用集合,集合对于数组有什么样的区别,这样我们才能把集合理解透彻。
集合与数组的区别在我上一篇Java小白记录学习路线(java进阶)-----Collection、泛型中提到过,集合是一个长度可变的数组,而不同的集合又有不同的特点,这些不同特点的原理就来自于数据结构。
数据结构可以应用在现实世界的存储,应用在我们使用的工具和建模。
就举一个简单的例子,咱们平时开车,你不知道发动机,变速箱是怎么运行的,经过训练,依然能开走车,也可以开的很好,但是你不能像赛车手那样真正的把车开的出神入化,车出了毛病,你也不知道怎么办,就只会开车。在这里java就相当于车,数据结构就相当于各种部件,也就是说只有学好数据结构,才能成为更好的程序员。
1.2 常见的数据结构
数据存储的常用结构有:栈、队列、数组、链表和红黑树。我们分别来了解一下:
栈
- 栈:stack,又称堆栈,它是运算受限的线性表,运算受限是指仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
知识补充:线性表是一种基础简单的数据结构,它就像一条线一样,把数据元素串起来,除第一个元素和最后一个元素之外,所有元素首尾相接(大部分,因为有循环的情况出现)
简单的说:采用该结构的集合,对元素的存取有如下的特点
-
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。
-
栈的入口、出口的都是栈的顶端位置。
这里两个名词需要注意: -
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
-
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
- 队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
- 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。
- 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
数组
- 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
查找元素快:通过索引,可以快速访问指定位置的元素
-
增删元素慢
-
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。
-
指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。
总的来说,在增加和删除的时候,因为数组需要有复制的操作,所以会很慢
链表
- 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域(data),另一个是存储下一个结点地址的指针域(next)。我们常说的链表结构有单向链表与双向链表,那么这里介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
多个结点之间,通过地址进行连接。像火车一样,一节连接着一节,每节之间又连接着,可以通过车厢号连接。
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
-
增删元素快:
-
增加元素:只需要修改连接下个元素的地址即可。
-
删除元素:只需要修改连接下个元素的地址即可。
因为链表在创建时,不会有固定的编号,所以只能从节点一点点向后查找
-
红黑树
- 二叉树:binary tree ,是每个结点不超过2的有序树(tree) 。
简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。
- 二叉树是每个节点最多有两个子树的树结构
- 顶上的叫根结点
- 两边的节点被称作“左子树”和“右子树”
如图(略有一丝简陋):
我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
在高中我们学过植物的去除顶端优势,这样生长素就会分泌到侧芽,促进侧芽生长,而红黑树其实就是去除二叉查找树顶端优势的解决方案,从而趋于平衡。
红黑树的约束:
-
节点可以是红色的或者黑色的
-
根节点是黑色的
-
叶子节点(特指空节点)是黑色的
-
每个红色节点的子节点都是黑色的
-
任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
红黑树的特点(如果不理解,可以根据约束试着画一下):
速度特别快,趋近平衡树,它是一个很平衡的状态。
如图:
List集合
让我们分析一下Collection中的常用几个子类(java.util.List
集合、java.util.Set
集合)。
2.1 List接口介绍
List接口特点:
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
- 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
1.2 List接口中常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
List的子类
3.1 ArrayList集合
java.util.ArrayList
集合数的存储结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。之前的文章提到过,就不做过多介绍
注意:不要所有的需求都用ArrayList集合
3.2 LinkedList集合
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是一个双向链表(双向链表与链表的区别就是,它两端都有指针,用来指向前一个节点或下一个)
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:
public void addFirst(E e)
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。public E getFirst()
:返回此列表的第一个元素。public E getLast()
:返回此列表的最后一个元素。public E removeFirst()
:移除并返回此列表的第一个元素。public E removeLast()
:移除并返回此列表的最后一个元素。public E pop()
:从此列表所表示的堆栈处弹出一个元素。public void push(E e)
:将元素推入此列表所表示的堆栈。public boolean isEmpty()
:如果列表不包含元素,则返回true。
方法演示:
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
//添加元素
link.addFirst("da1");
link.addFirst("da2");
link.addFirst("da3");
System.out.println(link);
// 获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// 删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
while (!link.isEmpty()) { //判断集合是否为空
System.out.println(link.pop()); //弹出集合中的栈顶元素
}
System.out.println(link);
}
}