day03 【List、Set、数据结构、
Collections】
主要内容
数据结构
List集合
Set集合
Collections
第⼀章 数据结构
2.1 数据结构有什么⽤?
当你⽤着java⾥⾯的容器类很爽的时候,你有没有想过,怎么ArrayList就像⼀个⽆限扩充的数组,也好
像链表之类的。好⽤吗?好⽤,这就是数据结构的⽤处,只不过你在不知不觉中使⽤了。
现实世界的存储,我们使⽤的⼯具和建模。每种数据结构有⾃⼰的优点和缺点,想想如果Google的数据
⽤的是数组的存储,我们还能⽅便地查询到所需要的数据吗?⽽算法,在这么多的数据中如何做到最快
的插⼊,查找,删除,也是在追求更快。
我们java是⾯向对象的语⾔,就好似⾃动档轿⻋,C语⾔好似⼿动档吉普。数据结构呢?是变速箱的⼯
作原理。你完全可以不知道变速箱怎样⼯作,就把⾃动档的⻋⼦从 A点 开到 B点,⽽且未必就⽐懂得的
⼈慢。写程序这件事,和开⻋⼀样,经验可以起到很⼤作⽤,但如果你不知道底层是怎么⼯作的,就永
远只能开⻋,既不会修⻋,也不能造⻋。当然了,数据结构内容⽐较多,细细的学起来也是相对费功夫
的,不可能达到⼀蹴⽽就。我们将常⻅的数据结构:堆栈、队列、数组、链表和红⿊树 这⼏种给⼤家介
绍⼀下,作为数据结构的⼊⻔,了解⼀下它们的特点即可。
2.2 常⻅的数据结构数据存储的常⽤结构有:栈、队列、数组、链表和红⿊树。我们分别来了解⼀下:
栈
栈:stack,⼜称堆栈,它是运算受限的线性表,其限制是仅允许在标的⼀端进⾏插⼊和删除操作,
不允许在其他任何位置进⾏添加、查找、删除等操作。
简单的说:采⽤该结构的集合,对元素的存取有如下的特点
先进后出(即,存进去的元素,要在后它后⾯的元素依次取出后,才能取出该元素)。例如,⼦弹
压进弹夹,先压进去的⼦弹在下⾯,后压进去的⼦弹在上⾯,当开枪时,先弹出上⾯的⼦弹,然后
才能弹出下⾯的⼦弹。
栈的⼊⼝、出⼝的都是栈的顶端位置。
这⾥两个名词需要注意:
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底⽅向移动⼀个位
置。
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶⽅向移动⼀个位置。
队列
队列:queue,简称队,它同堆栈⼀样,也是⼀种运算受限的线性表,其限制是仅允许在表的⼀端
进⾏插⼊,⽽在表的另⼀端进⾏删除。
简单的说,采⽤该结构的集合,对元素的存取有如下的特点:
先进先出(即,存进去的元素,要在后它前⾯的元素依次取出后,才能取出该元素)。例如,⼩⽕
⻋过⼭洞,⻋头先进去,⻋尾后进去;⻋头先出来,⻋尾后出来。
队列的⼊⼝、出⼝各占⼀侧。例如,下图中的左侧为⼊⼝,右侧为出⼝。数组
数组:Array,是有序的元素序列,数组是在内存中开辟⼀段连续的空间,并在此空间存放元素。就
像是⼀排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租
房⼦的⼈。
简单的说,采⽤该结构的集合,对元素的存取有如下的特点:
查找元素快:通过索引,可以快速访问指定位置的元素
增删元素慢
指定索引位置增加元素:需要创建⼀个新数组,将指定新元素存储在指定索引位置,再把原
数组元素根据索引,复制到新数组对应索引的位置。如下图
指定索引位置删除元素:需要创建⼀个新数组,把原数组元素根据索引,复制到新数组对应
索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图链表
链表:linked list,由⼀系列结点node(链表中每⼀个元素称为结点)组成,结点可以在运⾏时i动态
⽣成。每个结点包括两个部分:⼀个是存储数据元素的数据域,另⼀个是存储下⼀个结点地址的指
针域。我们常说的链表结构有单向链表与双向链表,那么这⾥给⼤家介绍的是单向链表。
简单的说,采⽤该结构的集合,对元素的存取有如下的特点:
多个结点之间,通过地址进⾏连接。例如,多个⼈⼿拉⼿,每个⼈使⽤⾃⼰的右⼿拉住下个⼈的左
⼿,依次类推,这样多个⼈就连在⼀起了。
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
增删元素快:
增加元素:只需要修改连接下个元素的地址即可。
在这里插入图片描述
删除元素:只需要修改连接下个元素的地址即可。
红⿊树
⼆叉树:binary tree ,是每个结点不超过2的有序树(tree) 。
简单的理解,就是⼀种类似于我们⽣活中树的结构,只不过每个结点上都最多只能有两个⼦结点。
⼆叉树是每个节点最多有两个⼦树的树结构。顶上的叫根结点,两边被称作“左⼦树”和“右⼦树”。
如图:我们要说的是⼆叉树的⼀种⽐较有意思的叫做红⿊树,红⿊树本身就是⼀颗⼆叉查找树,将节点插⼊
后,该树仍然是⼀颗⼆叉查找树。也就意味着,树的键值仍然是有序的。
红⿊树的约束:
- 节点可以是红⾊的或者⿊⾊的
- 根节点是⿊⾊的
- 叶⼦节点(特指空节点)是⿊⾊的
- 每个红⾊节点的⼦节点都是⿊⾊的
- 任何⼀个节点到其每⼀个叶⼦节点的所有路径上⿊⾊节点数相同
红⿊树的特点:
速度特别快,趋近平衡树,查找叶⼦元素最少和最多次数不多于⼆倍
第⼆章 List集合
我们掌握了Collection接⼝的使⽤后,再来看看Collection接⼝中的⼦类,他们都具备那些特性呢?
接下来,我们⼀起学习Collection中的常⽤⼏个⼦类( java.util.List 集合、 java.util.Set 集
合)。
1.1 List接⼝介绍
java.util.List 接⼝继承⾃ Collection 接⼝,是单列集合的⼀个重要分⽀,习惯性地会将实现了
List 接⼝的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以⼀种线性⽅式进⾏
存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有⼀个特点就是元素有
序,即元素的存⼊顺序和取出顺序⼀致。
看完API,我们总结⼀下:
List接⼝特点: - 它是⼀个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就
是按照11、22、33的顺序完成的)。2. 它是⼀个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是⼀个道
理)。 - 集合中可以有重复的元素,通过元素的equals⽅法,来⽐较是否为重复的元素。
tips:我们在基础班的时候已经学习过List接⼝的⼦类java.util.ArrayList类,该类中的⽅法都是来⾃
List中定义。
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集合特有的⽅法都是跟索引相关,我们在基础班都学习过,那么我们再来复习⼀遍吧:
public class ListDemo {
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 往 尾部添加 指定元素
list.add(“图图”);
list.add(“⼩美”);
list.add(“不⾼兴”);
System.out.println(list);
// add(int index,String s) 往指定位置添加
list.add(1,“没头脑”);
System.out.println(list);
// String remove(int index) 删除指定位置元素 返回被删除元素
// 删除索引位置为2的元素
System.out.println(“删除索引位置为2的元素”);
System.out.println(list.remove(2));
System.out.println(list);
// String set(int index,String s)
// 在指定位置 进⾏ 元素替代(改)
// 修改指定位置元素
list.set(0, “三⽑”);
System.out.println(list);
// String get(int index) 获取指定位置元素第三章 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() :返回此列表的最后⼀个元素。
// 跟size() ⽅法⼀起⽤ 来 遍历的
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
//还可以使⽤增强for
for (String string : list) {
System.out.println(string);
}
}
}public E removeFirst() :移除并返回此列表的第⼀个元素。
public E removeLast() :移除并返回此列表的最后⼀个元素。
public E pop() :从此列表所表示的堆栈处弹出⼀个元素。
public void push(E e) :将元素推⼊此列表所表示的堆栈。
public boolean isEmpty() :如果列表不包含元素,则返回true。
LinkedList是List的⼦类,List中的⽅法LinkedList都是可以使⽤,这⾥就不做详细介绍,我们只需要了
解LinkedList的特有⽅法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使⽤。(了解即
可)
⽅法演示:
第四章 Set接⼝
java.util.Set 接⼝和 java.util.List 接⼝⼀样,同样继承⾃ Collection 接⼝,它
与 Collection 接⼝中的⽅法基本⼀致,并没有对 Collection 接⼝进⾏功能上的扩充,只是⽐
Collection 接⼝更加严格了。与 List 接⼝不同的是, Set 接⼝中元素⽆序,并且都会以某种规则保
证存⼊的元素不出现重复。
Set 集合有多个⼦类,这⾥我们介绍其中的 java.util.HashSet 、 java.util.LinkedHashSet 这两
个集合。
tips:Set集合取出元素的⽅式可以采⽤:迭代器、增强for。
3.1 HashSet集合介绍
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
//添加元素
link.addFirst(“abc1”);
link.addFirst(“abc2”);
link.addFirst(“abc3”);
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);
}
}java.util.HashSet 是 Set 接⼝的⼀个实现类,它所存储的元素是不可重复的,并且元素都是⽆序的
(即存取顺序不⼀致)。
HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证
元素唯⼀性的⽅式依赖于: hashCode 与 equals ⽅法。
我们先来使⽤⼀下Set集合存储,看下现象,再进⾏原理的讲解:
输出结果如下,说明集合中不能存储重复元素:
tips:根据结果我们发现字符串"cba"只存储了⼀个,也就是说重复的元素set集合不存储。
2.2 HashSet集合存储数据的结构(哈希表)
什么是哈希表呢?
在JDK1.8之前,哈希表底层采⽤数组+链表实现,即使⽤链表处理冲突,同⼀hash值的链表都存储在⼀
个链表⾥。但是当位于⼀个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率
较低。⽽JDK1.8中,哈希表存储采⽤数组+链表+红⿊树实现,当链表⻓度超过阈值(8)时,将链表转
换为红⿊树,这样⼤⼤减少了查找时间。
public class HashSetDemo {
public static void main(String[] args) {
//创建 Set集合
HashSet set = new HashSet();
//添加元素
set.add(new String(“cba”));
set.add(“abc”);
set.add(“bac”);
set.add(“cba”);
//遍历
for (String name : set) {
System.out.println(name);
}
}
}
cba
abc
bac简单的来说,哈希表是由数组+链表+红⿊树(JDK1.8增加了红⿊树部分)实现的,如下图所示。
总⽽⾔之,JDK1.8引⼊红⿊树⼤程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元
素的唯⼀,其实就是根据对象的hashCode和equals⽅法来决定的。如果我们往集合中存放⾃定义的对
象,那么保证其唯⼀,就必须复写hashCode和equals⽅法建⽴属于当前对象的⽐较⽅式。
2.3 LinkedHashSet
我们知道HashSet保证元素唯⼀,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下⾯有⼀个⼦类 java.util.LinkedHashSet ,它是链表和哈希表组合的⼀个数据存储结
构。
演示代码如下:
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set set = new LinkedHashSet();
set.add(“bbb”);
set.add(“aaa”);
set.add(“abc”);
set.add(“bbc”);
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
结果:
bbb
aaa
abc
bbc