day13_JAVAOOP
课程目标
1. 【理解】集合的体系结构
2. 【掌握】Collection集合中常用的方法
3. 【理解】Iterator迭代器
4. 【掌握】增强for的使用
5. 【理解】List集合的特点
6. 【掌握】List集合中特有的方法
7. 【理解】LinkedList集合的特点
8. 【理解】LinkedList集合中特有的方法
9. 【理解】数据结构
集合概述
什么是集合
集合:集合是java中提供的一种容器,可以用来存储多个数据,并且可以存储任意类型的数据!
- 集合和数组既然都是容器,它们有啥区别呢?
- 数组的长度是固定的。集合的长度是可变的。
- 数组中存储的是同一类型的元素,可以存储基本数据类型值。
- 集合存储的都是对象。而且对象的类型可以不一致,不能存放基本数据类型
集合体系
JAVASE提供了满足各种需求的API,在使用这些API前,先了解其继承与接口操作架构,才能了解何时采用哪个类,以及类之间如何彼此合作,从而达到灵活应用。
集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection
和双列集合java.util.Map
,今天我们主要学习Collection
集合
- Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是
java.util.List
和java.util.Set
。其中,List
的特点是元素有序、元素可重复。Set
的特点是元素无序,而且不可重复。List
接口的主要实现类有java.util.ArrayList
和java.util.LinkedList
,Set
接口的主要实现类有java.util.HashSet
和java.util.LinkedHashSet
。
从上面的描述可以看出JDK中提供了丰富的集合类库,为了便于初学者进行系统地学习,接下来通过一张图来描述整个集合类的继承体系。
Collection集合
Collection概述
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。
Collection常用方法
方法名 | 说明 |
public boolean add(E e) | 把给定的对象e添加到当前集合中 。E:element 元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除e。 |
public boolean contains(E e) | 判断当前集合中是否包含给定的对象e。 |
public boolean isEmpty() | 判断当前集合是否为空。 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中。 |
public void clear() | 清空集合中所有的元素。 |
代码演示
import java.util.ArrayList; import java.util.Collection; public class Demo1Collection { public static void main(String[] args) { // 创建集合对象 // 使用多态形式 Collection<String> coll = new ArrayList<String>(); // 使用方法 // 添加功能 boolean add(String s) coll.add("小李广"); coll.add("扫地僧"); coll.add("石破天"); System.out.println(coll); // boolean contains(E e) 判断o是否在集合中存在 System.out.println("判断 扫地僧 是否在集合中"+coll.contains("扫地僧")); //boolean remove(E e) 删除在集合中的o元素 System.out.println("删除石破天:"+coll.remove("石破天")); System.out.println("操作之后集合中元素:"+coll); // size() 集合中有几个元素 System.out.println("集合中有"+coll.size()+"个元素"); // Object[] toArray()转换成一个Object数组 Object[] objects = coll.toArray(); // 遍历数组 for (int i = 0; i < objects.length; i++) { System.out.println(objects[i]); } // void clear() 清空集合 coll.clear(); System.out.println("集合中内容为:"+coll); // boolean isEmpty() 判断是否为空 System.out.println(coll.isEmpty()); } }
Iterator迭代器
为什么要有Iterator
由于Collection集合,是没有索引的。如果我们遍历Collection集合,使用for循环是无法使用的。我们可以借Collection中的toArray方法转换成数组,来遍历集合!这种方式也能实现,但总觉得不太舒服,这时,JDK提供了一个Iterator接口来专门用于遍历集合。
迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于"公交车上的售票员”、“火车上的乘务员”、“空姐”。
Iterator概述
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator
。Iterator
接口也是Java集合中的一员,但它与Collection
、Map
接口有所不同,Collection
接口与Map
接口主要用于存储元素,而Iterator
主要用于迭代访问(即遍历)Collection
中的元素,因此Iterator
对象也被称为迭代器。
什么是迭代
迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
获取Iterator
想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作。在Collection集合中,专门提供了一个方法,用来获取Iterator
public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。
- Iterator接口的常用方法
方法名 | 说明 |
public E next() | 返回迭代的下一个元素。 |
public boolean hasNext() | 如果仍有元素可以迭代,则返回 true。 |
代码演示
public class IteratorDemo { public static void main(String[] args) { // 使用多态方式 创建对象 Collection<String> coll = new ArrayList<String>(); // 添加元素到集合 coll.add("串串星人"); coll.add("吐槽星人"); coll.add("汪星人"); //遍历 //使用迭代器 遍历 每个集合对象都有自己的迭代器 Iterator<String> it = coll.iterator(); // 泛型指的是 迭代出 元素的数据类型 while(it.hasNext()){ //判断是否有迭代元素 String s = it.next();//获取迭代出的元素 System.out.println(s); } } }
迭代器的实现原理
我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,接下来通过一个图例来演示Iterator对象迭代元素的过程:
在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
增强for
什么是增强for
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
增强for格式
for(元素的数据类型 变量 : Collection集合or数组的变量名){ //写操作代码 }
它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
增强for遍历数组和集合
- 遍历数组
public class NBForDemo1 { public static void main(String[] args) { int[] arr = {3,5,6,87}; //使用增强for遍历数组 for(int a : arr){//a代表数组中的每个元素 System.out.println(a); } //数组的另一种遍历方式(jdk8以上才能使用) Arrays.stream(str).forEach(System.out::println); } }
- 遍历集合
public class NBForDemo2 { public static void main(String[] args) { Collection<String> coll = new ArrayList<String>(); coll.add("小河神"); coll.add("老河神"); coll.add("神婆"); //使用增强for遍历 for(String s :coll){//接收变量s代表 代表被遍历到的集合元素 System.out.println(s); } //集合的另一种遍历方式(jdk8以上才能使用) con.forEach(System.out::println); } }
新for循环必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现。
List集合
什么是List集合
java.util.List
接口继承自Collection
接口,是单列集合的一个重要分支,习惯性地会将实现了List
接口的对象称为List集合。
在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List集合的特点
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
- 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
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) | 用指定元素替换集合中指定位置的元素,返回值的更新前的元素。 |
public class ListDemo { public static void main(String[] args) { // 创建List集合对象 List<String> list = new ArrayList<String>(); // 往 尾部添加 指定元素 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); //获取集合的元素个数 int size = list.size(); System.out.println(size); //根索引来获取元素 // String get(int index) 获取指定位置元素 String s = list.get(0); System.out.println(s); // 跟size() 方法一起用 来 遍历的 for(int i = 0;i<list.size();i++){ System.out.println(list.get(i)); } //还可以使用增强for for (String string : list) { System.out.println(string); } } }
List集合的实现类
List的实现类特点: ArrayList: 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 LinkedList: 底层数据结构是链表,查询慢,增删快 线程不安全,效率高 Vector: 底层数据结构是数组,查询快,增删慢 线程安全,效率低
ArrayList集合
ArrayList: 底层数据结构是数组,查询快,增删慢 线程不安全,效率高
- ArrayList集合中常用方法
方法名 | 说明 |
public void add(int index, E element) | 将指定的元素,添加到该集合中的指定位置上。 |
public E get(int index) | 返回集合中指定位置的元素·。 |
public E remove(int index) | 移除列表中指定位置的元素, 返回的是被移除的元素。 |
public E set(int index, E element) | 用指定元素替换集合中指定位置的元素,返回值的更新前的元素。 |
public boolean add(E e) | 将指定的元素添加到此列表的尾部 |
/ ArrayList 存储自定义对象并遍历 / public class ArrayListDemo2 { public static void main(String[] args) { List<Student> list = new ArrayList<>(); Student s1 = new Student(); s1.setId(11); s1.setName("小包面"); Student s2 = new Student(); s2.setId(22); s2.setName("小肉丝"); list.add(s1); list.add(s2); System.out.println(list); Iterator<Student> it = list.iterator(); while (it.hasNext()){ //注意返回的类型为是Student对象 Student stu = it.next(); System.out.println(stu.getId()); System.out.println(stu.getName()); } System.out.println("====================="); for (Student s : list){ System.out.println(s.getName()+": "+s.getId()); } System.out.println("====================="); for(int x = 0; x < list.size(); x++){ Student stu = list.get(x); System.out.println(stu.getId()+": "+stu.getName()); } } }
LinkedList集合
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。 |
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。
LinkedList常用方法演示
/* * LinkedList的特有功能: * A:添加功能 * public void addFirst(Object e) * public void addLast(Object e) * B:获取功能 * public Object getFirst() * public Obejct getLast() * C:删除功能 * public Object removeFirst() * public Object removeLast() */ public class LinkedListDemo { public static void main(String[] args) { LinkedList<String> link = new LinkedList<String>(); // 添加元素 link.add("hello"); link.add("world"); link.add("java"); //添加元素 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); } }
Vector集合
Vector: 底层数据结构是数组,查询快,增删慢 线程安全,效率低
/* * Vector: vector是list的实现类,list中所有方法vector都可以使用,这里就介绍vector特有方法 * 1:添加功能 * public void addElement(Object obj) -- add() * 2:获取功能 * public Object elementAt(int index) -- get() * public Enumeration elements() -- Iterator iterator() * boolean hasMoreElements() -- hasNext() * Object nextElement() -- next() */ public class VectorDemo { public static void main(String[] args) { // 创建集合对象 Vector v = new Vector(); // 添加功能 v.addElement("hello"); v.addElement("world"); v.addElement("java"); // 遍历 for (int x = 0; x < v.size(); x++) { String s = (String) v.elementAt(x); System.out.println(s); } System.out.println("------------------"); //了解 Enumeration en = v.elements(); // 返回的是实现类的对象 while (en.hasMoreElements()) { String s = (String) en.nextElement(); System.out.println(s); } } }
list集合的总结
List的子类特点: ArrayList: 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 LinkedList: 底层数据结构是链表,查询慢,增删快 线程不安全,效率高 Vector: 底层数据结构是数组,查询快,增删慢 线程安全,效率低 list集合特点:有序可重复 今后我们如何选择list的三个子类? 如果你想查询快:选ArrayListVector 如果你想增删快:选LinkedList 如果你想要效率高:选ArrayList,LinkedList 如果你想要效率低:选Vector 如果你想要线程安全:选Vector 如果你想要线程不安全:选ArrayList,LinkedList 以上都是分业务情况来决定你要用哪一个集合。如果你实在不知道该如何选择,你就选译:ArrayList
数据结构
数据结构有什么用
当你用着java里面的容器类很爽的时候,你有没有想过,怎么ArrayList就像一个无限扩充的数组,也好像链表之类的。好用吗?好用,这就是数据结构的用处,只不过你在不知不觉中使用了。
现实世界的存储,我们使用的工具和建模。每种数据结构有自己的优点和缺点,想想如果Google的数据用的是数组的存储,我们还能方便地查询到所需要的数据吗?而算法,在这么多的数据中如何做到最快的插入,查找,删除,也是在追求更快。
我们java是面向对象的语言,就好似自动档轿车,C语言好似手动档吉普。数据结构呢?是变速箱的工作原理。你完全可以不知道变速箱怎样工作,就把自动档的车子从 A点 开到 B点,而且未必就比懂得的人慢。写程序这件事,和开车一样,经验可以起到很大作用,但如果你不知道底层是怎么工作的,就永远只能开车,既不会修车,也不能造车。当然了,数据结构内容比较多,细细的学起来也是相对费功夫的,不可能达到一蹴而就。我们将常见的数据结构:堆栈、队列、数组、链表和红黑树 这几种给大家介绍一下,作为数据结构的入门,了解一下它们的特点即可。
常见的数据结构
数据存储的常用结构有:栈、队列、数组、链表和红黑树。我们分别来了解一下:
栈
栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
- 栈的特点
- 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
- 栈的入口、出口的都是栈的顶端位置。
- 栈的名词
- 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
- 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
- 队列的特点
- 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
- 队列的入口、出口各占一侧。。例如,下图中的左侧为入口,右侧为出口。
数组
数组特点:查询快 , 增删慢
- 数组的特点
- 查找元素快:通过索引,可以快速访问指定位置的元素
- 增删元素慢
- 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置
- **指定索引位置删除元素:**需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。
链表
链表特点:查询慢 , 增删快
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
- 单向链表数据格式
- 链表的特点
多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
红黑树
- 二叉搜索树:binary tree ,是每个结点不超过2的有序树(tree)
简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。
二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。
查找某一个节点,我们必须从根节点查找。
1、查找的值比当前节点大,则搜索右子树
2、查找的值比当前节点小,则搜索左子树
3、查找的值等于当前值,停止搜索
4、查找最小值先找根节点的左边查询,一直找到左节点的节点,反之查找最大值从根节点右边查询一直到右节点的节点
插入某一个节点,我们必须从根节点查找。
插入的值要从根节点开始比较,小于根节点,在左子树比较。大于根节点,在右子树比较
插入某一个节点,根据根节点比较,找到该节点的位置直接删除,之后的其他节点再挂上去
- 红黑树
二叉树的一种比较有意思的叫做红黑树(红黑树是一种自平衡的二叉树),红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
- 红黑树的约束
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
- 红黑树的三个操作
- 左旋
- 右旋
- 变色
- 红黑树的特点
- 速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍
- 当我们查找和插入,删除数据相对频繁的时候效率(给二叉树一样)
插入的值要从根节点开始比较,小于根节点,在左子树比较。大于根节点,在右子树比较
插入某一个节点,根据根节点比较,找到该节点的位置直接删除,之后的其他节点再挂上去
- 红黑树
二叉树的一种比较有意思的叫做红黑树(红黑树是一种自平衡的二叉树),红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
- 红黑树的约束
- 节点可以是红色的或者黑色的
- 根节点是黑色的
- 叶子节点(特指空节点)是黑色的
- 每个红色节点的子节点都是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
- 红黑树的三个操作
- 左旋
- 右旋
- 变色
- 红黑树的特点
- 速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍
- 当我们查找和插入,删除数据相对频繁的时候效率(给二叉树一样)