【Collection、List、泛型、数据结构】
day05【Collection、List、泛型、数据结构】
今日内容
- Collection集合-------->重点掌握
- 继承体系
- 常用方法
- 迭代器
- 实现原理
- 基本使用
- 增强for循环-------->重点掌握
- 泛型
- 定义含有泛型的类,接口,方法
- 使用含有泛型的类,接口,方法-------->重点掌握
- 泛型通配符
- 数据结构----->了解
- 常见数据结构存储数据的特点
- List集合-------->重点掌握
- 特点
- 常用方法
- 子类使用
第一章 Collection集合
1.1 集合概述
- 概述: 集合其实就是一种容器,可以用来存储多个引用类型的数据
- 分类: 单列集合,双列集合
- 单列集合: 以单个单个元素进行存储
- 双列集合: 以键值对的方式进行存储
- 集合与数组的区别:
- 长度:
- 数组长度是固定的
- 集合长度是不固定的
- 存储范围:
- 数组可以存储基本类型+引用类型 eg; int[],String[]
- 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型 eg; ArrayList ,ArrayList
- 长度:
1.2 单列集合常用类的继承体系
单列集合: 以单个单个元素进行存储
单列集合继承体系:
Collection接口是所有单列集合的根接口,也就意味着所有的单列集合都实现了Collection接口
- List接口继承Collection接口: List集合元素有索引,元素存取有序,元素可重复
- ArrayList类: 数组存储结构, 查询快,增删慢
- LinkedList类: 链表存储结构,查询慢,增删快
- …
- Set接口继承Collection接口: Set集合元素没有索引 , 元素不可重复(唯一)
- HashSet类: 哈希表结构,由哈希表保证元素唯一,元素存取无序,不可以排序
- LinkedHashSet类:: 链表+哈希表结构,由哈希表保证元素唯一,由链表保证元素存取有序,不可以排序
- TreeSet类: 二叉树结构,可以对元素进行排序
- …
- HashSet类: 哈希表结构,由哈希表保证元素唯一,元素存取无序,不可以排序
- List接口继承Collection接口: List集合元素有索引,元素存取有序,元素可重复
1.3 Collection 常用功能
Collection是接口,只能通过其子类创建对象
Collection是所有单列集合的顶层父接口,所以所有单列集合都拥有Collection中的方法
常用方法:
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中
public class Test {public static void main(String[] args) {// 创建Collection集合对象,限制集合元素类型为String类型Collection<String> col = new ArrayList<>();// - public boolean add(E e): 把给定的对象添加到当前集合中 。col.add("王宝强");col.add("贾乃亮");col.add("谢霆锋");col.add("陈羽凡");System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 谢霆锋, 陈羽凡]// - public void clear() :清空集合中所有的元素。// col.clear();// System.out.println("col:" + col);// col:[]// - public boolean remove(E e): 把给定的对象在当前集合中删除。boolean res1 = col.remove("谢霆锋");System.out.println("col:" + col);// col:[王宝强, 贾乃亮, 陈羽凡]System.out.println("res1:" + res1);// res1: true// - public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。boolean res2 = col.contains("贾乃亮");boolean res3 = col.contains("谢霆锋");System.out.println("res2:" + res2);// res2: trueSystem.out.println("res3:" + res3);// res3: false// - public boolean isEmpty(): 判断当前集合是否为空。如果集合中没有元素返回true,否则返回falseboolean res4 = col.isEmpty();System.out.println("res4:" + res4);// res4: false// 清空集合中的所有元素// col.clear();// boolean res5 = col.isEmpty();// System.out.println("res5:" + res5);// res5: true// - public int size(): 返回集合中元素的个数。System.out.println("集合元素的个数:" + col.size());// 3// - public Object[] toArray(): 把集合中的元素,存储到数组中Object[] arr = col.toArray();System.out.println("arr:"+ Arrays.toString(arr));// arr:[王宝强, 贾乃亮, 陈羽凡]} }
第二章 Iterator迭代器
2.1 Iterator接口
迭代的概念
- 概述:迭代即Collection集合元素的通用获取方式。**在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。**这种取出方式专业术语称为迭代。
- 迭代的步骤:
- 获取迭代器对象
- 使用迭代器对象判断集合中是否有元素可以取出
- 如果有元素可以取出,就直接取出来该元素,如果没有元素可以取出,就结束迭代
获取迭代器对象
Collection集合提供了一个获取迭代器的方法:
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
Iterator迭代器对象的常用方法
public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。public E next()
:返回迭代的下一个元素。void remove()
删除当前迭代出来的元素案例:
public class Test {public static void main(String[] args) {/*获取迭代器对象Collection集合提供了一个获取迭代器的方法:- public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。Iterator迭代器对象的常用方法- public boolean hasNext():如果仍有元素可以迭代,则返回 true。- public E next():返回迭代的下一个元素。- void remove()删除当前迭代出来的元素*/Collection<String> col = new ArrayList<>();// 往集合中添加元素col.add("王宝强");col.add("贾乃亮");col.add("谢霆锋");col.add("陈羽凡");// 通过集合对象获取对应的迭代器对象Iterator<String> it = col.iterator();// 循环判断是否有元素可以迭代while (it.hasNext()) {// 如果有,就在循环中取出可以迭代的元素String e = it.next();System.out.println("元素:"+e);// 需求:如果迭代出来的元素是谢霆锋,就删除该元素if ("谢霆锋".equals(e)){it.remove();}}System.out.println("col:"+col);// col:[王宝强, 贾乃亮, 陈羽凡]} }
2.2 迭代器的实现原理
- 迭代器的实现原理
我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
2.3 迭代器的常见问题
常见问题一
在进行集合元素获取时,如果集合中已经没有元素可以迭代了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。
public class Test2_迭代常见问题1 {public static void main(String[] args) {/*问题一:在进行集合元素获取时,如果集合中已经没有元素可以迭代了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常。*/Collection<String> col = new ArrayList<>();// 往集合中添加元素col.add("王宝强");col.add("贾乃亮");col.add("谢霆锋");col.add("陈羽凡");// 获取迭代器对象Iterator<String> it = col.iterator();// 迭代--->快捷键:ititwhile (it.hasNext()) {String e = it.next();System.out.println("元素:" + e);}System.out.println("--------");// 调用next方法// String next = it.next();// 报NoSuchElementException没有找到元素异常// System.out.println("next:" + next);// 需求:再迭代col集合中所有的元素// 获取迭代器对象Iterator<String> it1 = col.iterator();// 迭代--->快捷键:ititwhile (it1.hasNext()) {String e = it1.next();System.out.println("元素:" + e);}} }
解决方式: 使用集合从新获取一个新的迭代器对象来使用
常见问题二
在进行集合元素迭代时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.
public class Test3_迭代常见问题二 {public static void main(String[] args) {/*问题: 在进行集合元素迭代时,如果添加或移除集合中的元素,将无法继续迭代,将会抛出ConcurrentModificationException并发修改异常.*/Collection<String> col = new ArrayList<>();// 往集合中添加元素col.add("王宝强");col.add("贾乃亮");col.add("谢霆锋");col.add("陈羽凡");// 获取迭代器对象Iterator<String> it = col.iterator();// 迭代while (it.hasNext()) {String e = it.next();// 问题一: 迭代一个元素,就使用集合对象往集合中添加一个元素// col.add("itheima");// 报并发修改异常// 问题二: 迭代一个元素,就使用集合对象删除一个元素// col.remove(e);// 报并发修改异常// 迭代器可以一边迭代一边删除it.remove();}System.out.println("col:" + col);// col:[]} }
解决办法:
- 使用CopyOnWriteArrayList集合,就可以迭代的时候,往集合中添加或删除元素
- 使用迭代器的remove方法实现一边迭代,一边删除
2.4 增强for
概述: 增强for循环(foreach循环),是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和Collection集合
原理: 内部基于Iterator迭代器实现,所以在遍历的过程中,不能对集合中的元素进行增删操作,否则抛出ConcurrentModificationException并发修改异常
格式:
for(数据类型 变量名 : 数组名\集合名){}
案例:
public class Test4_增强for循环 {public static void main(String[] args) {/*for(数据类型 变量名 : 数组名\集合名){}*/// 需求1: 增强for循环遍历集合元素Collection<String> col = new ArrayList<>();// 往集合中添加元素col.add("王宝强");col.add("贾乃亮");col.add("谢霆锋");col.add("陈羽凡");// 快捷键: 集合名.forfor (String e : col) {System.out.println("e:" + e);}System.out.println("--------");// 需求1: 增强for循环遍历数组元素String[] arr = {"王宝强","贾乃亮","谢霆锋","陈羽凡"};// 快捷键: 数组名.forfor (String e : arr) {System.out.println("e:" + e);}} }
第三章 泛型
3.1 泛型的概述
概述: JDK5之后,新增了泛型(Generic)语法,可以在类、接口或方法中预支地使用未知的类型
简而言之: 泛型其实就是表示一种未知的数据类型,在使用的时候确定其具体的数据类型
表示方式: <泛型变量>
泛型的好处:
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败
- 避免了类型转换的麻烦
案例:
集合不使用泛型
可能会发生类型转换异常
避免类型转换异常,就需要先做类型判断,再转型—>比较麻烦
public class Test1集合不使用泛型 {public static void main(String[] args) {// 注意: 不指定集合元素的类型,元素的类型默认是Object类型// 创建ArrayList集合对象,不指定泛型的具体数据类型ArrayList list = new ArrayList();// 往集合中添加元素list.add("itheima");list.add(666);list.add(3.14);list.add(true);list.add("itcast");System.out.println("list:"+list);// list:[itheima, 666, 3.14, true, itcast]// 获取集合中所有字符串元素的长度for (Object obj : list) {// String str = (String) obj;// 发生类型转换异常// System.out.println(str.length());// 转换类型之前先判断if (obj instanceof String){String str = (String) obj;System.out.println(str.length());}}} }
集合使用泛型
概述:指定泛型的具体数据类型----->(只能是引用数据类型)
public class Test2集合使用泛型 {public static void main(String[] args) {// 注意: 使用集合的时候,建议通过泛型来限制集合元素类型一致// 创建ArrayList集合对象,指定泛型的具体数据类型为StringArrayList<String> list = new ArrayList<>();// 往集合中添加元素list.add("itheima");// list.add(666);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败// list.add(3.14);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败// list.add(true);// 编译报错,把运行时的类型转换异常,转移到了编译时期变成了编译失败list.add("itcast");System.out.println("list:"+list);// list:[itheima, itcast]// 获取集合中所有字符串元素的长度for (String s : list) {System.out.println(s.length());}} }
3.2 定义和使用含有泛型的类
定义含有泛型的类
格式:
public class 类名<泛型变量>{} 泛型变量: 可以是任意字母,eg: A,B,E,...a,b,....;一般写E
案例
// 含有泛型的类 public class MyGenericClass1<E> {E e;public void method1(E e) {System.out.println("e的类型:" + e.getClass());}public E method2(E e){return e;} }
使用含有泛型的类
创建含有泛型的类的对象的时候,指定泛型的具体数据类型(只能是引用数据类型)
案例
public class Test {public static void main(String[] args) {// 使用含有泛型的类// 创建含有泛型的类的对象MyGenericClass1<String> mc1 = new MyGenericClass1<>();mc1.method1("itheima");// 创建含有泛型的类的对象MyGenericClass1<Integer> mc2 = new MyGenericClass1<>();mc2.method1(100);} }
3.3 定义和使用含有泛型的方法
定义含有泛型的方法
格式:
修饰符 <泛型变量> 返回值类型 方法名(形参列名){方法体 } // 泛型变量:可以写任意字母,一般写T
案例:
public class MyGenericMethod {// T:表示未知的数据类型,调用方法的时候确定具体的数据类型public <T> T method1(T t){return t;}public <T> T method2(T t){return t;} }
使用含有泛型的方法
调用含有泛型方法的时候,确定泛型的具体数据类型
案例:
public class Test {public static void main(String[] args) {// 使用含有泛型的方法:调用含有泛型的方法,指定泛型的具体数据类型MyGenericMethod mg = new MyGenericMethod();// 调用method1,指定泛型的具体类型为IntegerInteger i = mg.method1(100);System.out.println("i:"+i);// i:100// 调用method1,指定泛型的具体类型为StringString str = mg.method1("itheima");System.out.println("str:"+str);// str:itheima} }
3.4 定义和使用含有泛型的接口
定义含有泛型的接口
格式:
public interface 接口名<泛型变量>{} // 泛型变量:可以是任意字母,一般写E
案例:
// 含有泛型的接口 public interface MyGenericInterface<E> {void method1(E e);public default E method2(E e){return e;}}
使用含有泛型的接口
方式一: 实现类实现接口的时候,确定接口泛型的具体数据类型
格式:
public class 类名 implements 接口名<具体的引用数据类型>{}
案例:
// 实现接口的时候,确定接口泛型的具体数据类型 class Imp implements MyGenericInterface<String>{@Overridepublic void method1(String s) {System.out.println("method1.."+s);}@Overridepublic String method2(String s) {System.out.println("method2..."+s);return s;} }
方式二:实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型
格式:
public class 类名<泛型变量> implements 接口名<泛型变量>{}
案例:
// 实现类实现接口的时候,不确定接口泛型的具体数据类型,而是创建实现类对象的时候确定泛型的具体数据类型 class Imp2<E> implements MyGenericInterface<E>{@Overridepublic void method1(E e) {System.out.println("e的类型:"+e.getClass());}@Overridepublic E method2(E e) {System.out.println("e的类型:"+e.getClass());return e;} }public class Test {public static void main(String[] args) {// 接口: 被实现// 创建实现类对象的时候确定泛型的具体数据类型Imp2<String> i1 = new Imp2<>();i1.method1("itheima");String res1 = i1.method2("itcast");System.out.println("-----");Imp2<Integer> i2 = new Imp2<>();i2.method1(100);Integer res2 = i2.method2(200);} }
3.5 泛型通配符
概述: 泛型通配符用问号表示(?)
为什么需要泛型通配符:
- 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
Collection<Object> list = new ArrayList<String>(); //错误格式,泛型不存在继承关系
- 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符
Collection<?> list 变量接收
- 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象
通配符基本使用
格式:
数据类型<?> 变量
注意:
- 如果使用了泛型通配符,那么该集合变量元素类型默认是Object类型
- 如果使用了泛型通配符,那么该集合变量只能取元素,无法增删元素
案例:
public class Test {public static void main(String[] args) {// Integer继承Number,Number继承Object,String继承ObjectArrayList<Object> list1 = new ArrayList<>();ArrayList<String> list2 = new ArrayList<>();ArrayList<Integer> list3 = new ArrayList<>();ArrayList<Number> list4 = new ArrayList<>();// 泛型本身不存在继承关系,不可以给已指定泛型的变量接收有其他泛型类型的对象// method1(list1);// 编译报错// method1(list2); // 正确// method1(list3);// 编译报错// method1(list4);// 编译报错method2(list1);method2(list2);method2(list3);method2(list4);}// 定义一个方法,可以接收任意ArrayList集合对象// 如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符public static void method2(ArrayList<?> list) {// 已指定泛型的变量System.out.println("执行了");// 注意: 如果使用了泛型通配符,那么该集合元素类型默认是Object类型for (Object obj : list) {}// 注意: 如果使用了泛型通配符,那么该集合变量只能取元素,无法增删元素// list.add(1000);// 编译报错}public static void method1(ArrayList<String> list) {// 已指定泛型的变量} }
通配符高级使用----受限泛型
上限:
- 格式:
<? extends 类名>
- 表示: 只接受该类类型或者其子类类型
- 格式:
下限:
- 格式:
<? super 类名>
- 表示: 只接受该类类型或者其父类类型
- 案例:
- 格式:
案例:
public class Test {public static void main(String[] args) {// Integer继承Number,Number继承Object,String继承ObjectArrayList<Object> list1 = new ArrayList<>();ArrayList<String> list2 = new ArrayList<>();ArrayList<Integer> list3 = new ArrayList<>();ArrayList<Number> list4 = new ArrayList<>();method1(list1);//method1(list2);// 编译报错method1(list3);method1(list4);//method2(list1);// 编译报错//method2(list2);// 编译报错method2(list3);method2(list4);}// 定义一个方法,只可以接收泛型是Integer或者其父类类型的ArrayList集合对象public static void method1(ArrayList<? super Integer> list){list.add(100);}// 定义一个方法,只可以接收泛型是Number或者其子类类型的ArrayList集合对象public static void method2(ArrayList<? extends Number> list){}}
第四章 数据结构
4.1 数据结构介绍
- 数据结构 :其实就是存储数据和表示数据的方式
- 常见的数据结构:栈、队列、数组、链表和红黑树
4.2 常见数据结构
栈
- 栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
栈的入口、出口的都是栈的顶端位置。
这里两个名词需要注意:
- 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
- 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
队列
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
- 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
- 队列的入口和出口在两侧。例如,下图中的左侧为入口,右侧为出口。
数组
- 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
查找元素快:通过索引,可以快速访问指定位置的元素
增删元素慢
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。
**指定索引位置删除元素:**需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。
链表
- 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。
增删元素快:只需要修改链接下一个元素的地址值即可
4.3 树基本结构介绍
树具有的特点
- 每一个节点有零个或者多个子节点
- 没有父节点的节点称之为根节点,一个树最多有一个根节点。
- 每一个非根节点有且只有一个父节点
名词 | 含义 |
---|---|
节点 | 指树中的一个元素 |
节点的度 | 节点拥有的子树的个数,二叉树的度不大于2 |
叶子节点 | 度为0的节点,也称之为终端结点 |
高度 | 叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高 |
层 | 根节点在第一层,以此类推 |
父节点 | 若一个节点含有子节点,则这个节点称之为其子节点的父节点 |
子节点 | 子节点是父节点的下一层节点 |
兄弟节点 | 拥有共同父节点的节点互称为兄弟节点 |
二叉树
如果树中的每个节点的子节点的个数不超过2,那么该树就是一个二叉树。
二叉查找树
二叉查找树的特点:
- 左子树上所有的节点的值均小于等于他的根节点的值
- 右子树上所有的节点值均大于或者等于他的根节点的值
- 每一个子节点最多有两个子树
案例演示(20,18,23,22,17,24,19)数据的存储过程;
遍历获取元素的时候可以按照"左中右"的顺序进行遍历: 17 , 18, 19,20,22,23,24
注意:二叉查找树存在的问题:会出现"瘸子"的现象,影响查询效率
平衡二叉树
概述
为了避免出现"瘸子"的现象,减少树的高度,提高我们的搜素效率,又存在一种树的结构:“平衡二叉树”
规则:它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
如下图所示:
如下图所示,左图是一棵平衡二叉树,根节点10,左右两子树的高度差是1,而右图,虽然根节点左右两子树高度差是0,但是右子树15的左右子树高度差为2,不符合定义,
所以右图不是一棵平衡二叉树。
旋转
在构建一棵平衡二叉树的过程中,当有新的节点要插入时,检查是否因插入后而破坏了树的平衡,如果是,则需要做旋转去改变树的结构。
左旋:
左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;
右旋:
将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点
举个例子,像上图是否平衡二叉树的图里面,左图在没插入前"19"节点前,该树还是平衡二叉树,但是在插入"19"后,导致了"15"的左右子树失去了"平衡",
所以此时可以将"15"节点进行左旋,让"15"自身把节点出让给"17"作为"17"的左树,使得"17"节点左右子树平衡,而"15"节点没有子树,左右也平衡了。如下图,
由于在构建平衡二叉树的时候,当有新节点插入时,都会判断插入后时候平衡,这说明了插入新节点前,都是平衡的,也即高度差绝对值不会超过1。当新节点插入后,
有可能会有导致树不平衡,这时候就需要进行调整,而可能出现的情况就有4种,分别称作左左,左右,右左,右右。
左左:只需要做一次右旋就变成了平衡二叉树。
右右:只需要做一次左旋就变成了平衡二叉树。
左右:先做一次分支的左旋,再做一次树的右旋,才能变成平衡二叉树。
右左:先做一次分支的右旋,再做一次数的左旋,才能变成平衡二叉树。
课上只讲解“左左”的情况
左左
左左:只需要做一次右旋就变成了平衡二叉树。
左左即为在原来平衡的二叉树上,在节点的左子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"10"节点的左子树"7",的左子树"4",插入了节点"5"或"3"导致失衡。
左左调整其实比较简单,只需要对节点进行右旋即可,如下图,对节点"10"进行右旋,
左右
左右:先做一次分支的左旋,再做一次树的右旋,才能变成平衡二叉树。
左右即为在原来平衡的二叉树上,在节点的左子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的左子树"7",的右子树"9",
插入了节点"10"或"8"导致失衡。
左右的调整就不能像左左一样,进行一次旋转就完成调整。我们不妨先试着让左右像左左一样对"11"节点进行右旋,结果图如下,右图的二叉树依然不平衡,而右图就是接下来要
讲的右左,即左右跟右左互为镜像,左左跟右右也互为镜像。
左右这种情况,进行一次旋转是不能满足我们的条件的,正确的调整方式是,将左右进行第一次旋转,将左右先调整成左左,然后再对左左进行调整,从而使得二叉树平衡。
即先对上图的节点"7"进行左旋,使得二叉树变成了左左,之后再对"11"节点进行右旋,此时二叉树就调整完成,如下图:
右左
右左:先做一次分支的右旋,再做一次数的左旋,才能变成平衡二叉树。
右左即为在原来平衡的二叉树上,在节点的右子树的左子树下,有新节点插入,导致节点的左右子树的高度差为2,如上即为"11"节点的右子树"15",的左子树"13",
插入了节点"12"或"14"导致失衡。
前面也说了,右左跟左右其实互为镜像,所以调整过程就反过来,先对节点"15"进行右旋,使得二叉树变成右右,之后再对"11"节点进行左旋,此时二叉树就调整完成,如下图:
右右
右右:只需要做一次左旋就变成了平衡二叉树。
右右即为在原来平衡的二叉树上,在节点的右子树的右子树下,有新节点插入,导致节点的左右子树的高度差为2,如下即为"11"节点的右子树"13",的左子树"15",插入了节点
"14"或"19"导致失衡。
右右只需对节点进行一次左旋即可调整平衡,如下图,对"11"节点进行左旋。
红黑树
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构,它是在1972年由Rudolf Bayer发明的,当时被称之为平衡二叉B树,后来,在1978年被
Leoj.Guibas和Robert Sedgewick修改为如今的"红黑树"。它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,可以是红或者黑;
红黑树不是高度平衡的,它的平衡是通过"红黑树的特性"进行实现的;
红黑树的特性:
- 每一个节点或是红色的,或者是黑色的。
- 根节点必须是黑色
- 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
- 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
如下图所示就是一个
在进行元素插入的时候,和之前一样; 每一次插入完毕以后,使用黑色规则进行校验,如果不满足红黑规则,就需要通过变色,左旋和右旋来调整树,使其满足红黑规则;
4.4 小结
- 栈结构: 先进后出
- 队列结构:先进先出
- 数组: 查询快,增删慢
- 链表; 查询慢,增删快
- 二叉查找树: 提供搜索效率
第五章 List接口
5.1 List接口介绍
- List接口的概述
- java.util.List接口继承自Collection接口,是单列集合的一个重要分支
- List接口的特点
- 它是一个元素存取有序的集合
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
- 集合中可以有重复的元素
5.2 List接口中常用方法
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集合特有的方法都是跟索引相关,我们在基础班都学习过。
List接口新增常用方法的使用
public class Test {public static void main(String[] args) {// 创建List集合对象,限制集合元素的类型为StringList<String> list = new ArrayList<>();// 往集合中添加元素的方法list.add("马蓉");list.add("李小璐");list.add("张柏芝");list.add("白百何");System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]// - public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。list.add(1, "潘金莲");System.out.println("list:" + list);// list:[马蓉, 潘金莲, 李小璐, 张柏芝, 白百何]// - public E get(int index):返回集合中指定位置的元素。String e = list.get(1);System.out.println("e:" + e);// e:潘金莲// - public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。String removeE = list.remove(1);System.out.println("被删除的元素:"+removeE);// 被删除的元素:潘金莲System.out.println("list:" + list);// list:[马蓉, 李小璐, 张柏芝, 白百何]// - public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。String setE = list.set(2, "董洁");System.out.println("被替换的元素:"+setE);// 被替换的元素:张柏芝System.out.println("list:" + list);// list:[马蓉, 李小璐, 董洁, 白百何]}
}
注意: 如果集合元素为Integer类型,那么删除的时候优先根据索引删除
public class Test2 {public static void main(String[] args) {/*public boolean remove(Object obj): 删除指定的元素。public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。*/// 创建List集合,限制集合元素的类型为IntegerList<Integer> list = new ArrayList<>();// 往集合中添加元素list.add(1);list.add(2);list.add(3);list.add(4);// 删除元素list.remove(2);// 优先根据索引删除System.out.println("list:" + list);// list:[1, 2, 4]} }
5.3 List的子类
ArrayList集合: 底层采用的是数组结构,查询快,增删慢
- 方法: 来自Collection,List
LinkedList集合; 底层采用的是链表结构,查询慢,增删快
方法: 来自Collection,List,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 class Test {public static void main(String[] args) {// 创建LinkedList集合,限制集合元素类型为StringLinkedList<String> list = new LinkedList<>();// 往集合中添加一些元素list.add("苍老师");list.add("小泽老师");list.add("波老师");System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]// public void addFirst(E e):将指定元素插入此列表的开头list.addFirst("吉泽老师");// list:[吉泽老师, 苍老师, 小泽老师, 波老师]System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师]// public void addLast(E e):将指定元素添加到此列表的结尾list.addLast("三村老师");System.out.println("list:"+list);// list:[吉泽老师, 苍老师, 小泽老师, 波老师, 三村老师]// public E getFirst():返回此列表的第一个元素System.out.println("第一个元素: "+list.getFirst());// 吉泽老师// public E getLast():返回此列表的最后一个元素System.out.println("最后一个元素: "+list.getLast());// 三村老师// public E removeFirst():移除并返回此列表的第一个元素String removeFirstE = list.removeFirst();System.out.println("removeFirstE:"+removeFirstE);// 吉泽老师// public E removeLast():移除并返回此列表的最后一个元素String removeLastE = list.removeLast();System.out.println("removeLastE:"+removeLastE);// 三村老师System.out.println("list:"+list);// list:[苍老师, 小泽老师, 波老师]// public E pop():从此列表所表示的堆栈处弹出一个元素(removeFirst方法)String popE = list.pop();System.out.println("popE:"+popE);// 苍老师// public void push(E e):将元素推入此列表所表示的堆栈(addFirst)list.push("明日花老师");System.out.println("list:"+list);// list:[明日花老师, 小泽老师, 波老师]} }
5.4 集合综合案例
需求:
按照斗地主的规则,完成洗牌发牌的动作。
具体规则:使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
分析:
造牌
1.创建一个集合,用来存储54张扑克牌
2.创建一个花色集合,用来存储4个花色
3.创建一个牌面值集合,用来存储13个牌面值
4.往存储扑克牌的集合中存储大小王
5.花色集合和牌面值集合循环嵌套,生成52张扑克牌
6.在循环中,把生成的扑克牌添加到存储扑克牌的集合中洗牌: 打乱集合元素顺序
发牌:
1.创建4个集合,用来存储玩家1,玩家2,玩家3,底牌的手牌,
2.循环遍历打乱顺序之后的存储54张扑克牌的集合
3.在循环中,判断索引
3.0 如果索引大于等于51,就取出的牌给底牌
3.1 如果索引%30,就取出的牌给玩家1
3.2 如果索引%31,就取出的牌给玩家2
3.3 如果索引%3==2,就取出的牌给玩家3
4.展示牌展示牌: 打印输出即可
实现:
public class Test {public static void main(String[] args) {// 造牌:// 1.创建一个集合,用来存储54张扑克牌ArrayList<String> pokerBox = new ArrayList<>();// 2.创建一个花色集合,用来存储4个花色ArrayList<String> colors = new ArrayList<>();colors.add("♠");colors.add("♥");colors.add("♣");colors.add("♦");// 3.创建一个牌面值集合,用来存储13个牌面值ArrayList<String> numbers = new ArrayList<>();numbers.add("A");numbers.add("K");numbers.add("Q");numbers.add("J");for (int i = 2; i <= 10; i++) {numbers.add(i + "");}// 4.往存储扑克牌的集合中存储大小王pokerBox.add("大王");pokerBox.add("小王");// 5.花色集合和牌面值集合循环嵌套,生成52张扑克牌for (String number : numbers) {for (String color : colors) {// 拼接牌String pai = color + number;// 6.在循环中,把生成的扑克牌添加到存储扑克牌的集合中pokerBox.add(pai);}}System.out.println("牌:" + pokerBox);System.out.println("牌:" + pokerBox.size());// 洗牌: 打乱集合元素的顺序// public static void shuffle(List<?> list) 随机打乱List集合中元素的顺序Collections.shuffle(pokerBox);System.out.println("牌:" + pokerBox);System.out.println("牌:" + pokerBox.size());// 发牌:// 1.创建4个集合,用来存储玩家1,玩家2,玩家3,底牌的手牌,ArrayList<String> play1 = new ArrayList<>();ArrayList<String> play2 = new ArrayList<>();ArrayList<String> play3 = new ArrayList<>();ArrayList<String> diPai = new ArrayList<>();// 2.循环遍历打乱顺序之后的存储54张扑克牌的集合for (int i = 0; i < pokerBox.size(); i++) {// 取出牌String pai = pokerBox.get(i);// 3.在循环中,判断索引// 3.0 如果索引大于等于51,就取出的牌给底牌if (i >= 51) {diPai.add(pai);} else if (i % 3 == 0) {// 3.1 如果索引%3==0,就取出的牌给玩家1play1.add(pai);} else if (i % 3 == 1) {// 3.2 如果索引%3==1,就取出的牌给玩家2play2.add(pai);} else if (i % 3 == 2) {// 3.3 如果索引%3==2,就取出的牌给玩家3play3.add(pai);}}// 4.展示牌System.out.println("玩家1:"+play1+",牌数;"+play1.size());System.out.println("玩家2:"+play2+",牌数;"+play2.size());System.out.println("玩家3:"+play3+",牌数;"+play3.size());System.out.println("底牌:"+diPai+",牌数;"+diPai.size());}
}
总结
必须练习:1.总结单列集合继承体系和各个单列集合的特点2.Collection集合的常用方法3.迭代器的基本使用,增强for循环的使用--->使用迭代器,增强for循环遍历集合元素4.使用含有泛型的类,含有泛型的接口,含有泛型的方法---->ArrayList<E>类,List<E>接口5.List集合的常用方法6.LinkedList集合的特有方法7.斗地主案例---->选做- 能够说出集合与数组的区别- 长度:- 数组长度是固定的- 集合长度是不固定的- 存储范围:- 数组可以存储基本类型+引用类型 eg; int[],String[]- 集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型- 能够使用Collection集合的常用功能- public boolean add(E e): 把给定的对象添加到当前集合中 。- public void clear() :清空集合中所有的元素。- public boolean remove(E e): 把给定的对象在当前集合中删除。- public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。- public boolean isEmpty(): 判断当前集合是否为空。- public int size(): 返回集合中元素的个数。- public Object[] toArray(): 把集合中的元素,存储到数组中- 能够使用迭代器对集合进行取元素1.获取迭代器,使用Collection的iterator()方法2.使用迭代器判断是否有元素可以迭代 hasNext()3.使用迭代器取出可以迭代的元素 next()- 能够使用增强for循环遍历集合和数组格式:for(数据类型 变量名: 数组名\集合名){}- 能够理解泛型上下限上限: <? extends 类名>下限: <? super 类名>- 能够阐述泛型通配符的作用如果想要使变量在未来接收有泛型定义的对象,又不确定泛型要定义的类型可以使用泛型通配符?- 能够说出常见的数据结构栈,队列,数组,链表,树- 能够说出数组结构特点
- 能够说出栈结构特点
- 能够说出队列结构特点
- 能够说出单向链表结构特点- 栈结构: 先进后出- 队列结构:先进先出- 数组: 查询快,增删慢- 链表; 查询慢,增删快- 二叉查找树: 提供搜索效率- 能够说出List集合特点元素有索引,元素存取有序,元素可以重复- 能够完成斗地主的案例 洗牌,发牌,造牌
【Collection、List、泛型、数据结构】相关推荐
- day07【Collection、泛型、数据结构】
day07[Collection.泛型.数据结构] 反馈和复习 1.老师多照顾一下基础差的哥们 2.老师讲的太慢了 3.学生管理系统3遍 今日内容 今日+明日+后日(非常重要!!主要讲各种集合!!) ...
- JAVA学习 API_day03(字符串编码, 泛型, 数据结构, Collection)
字符串编码, 泛型, 数据结构, Collection 1.字符串编码 2.泛型 3.数据结构 4.Collection 1.字符串编码 1.String -> 字符序列 (中文\英文) JVM ...
- 【Collection、泛型】
[Collection.泛型] 第一章 Collection集合 1.1 集合概述 集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既的区别: 数组的长度是固定的.集合的长度是 ...
- 二、Java 面向对象高级——Collection、泛型
day02[Collection.泛型] 主要内容 Collection集合 迭代器 增强for 泛型 教学目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合 ...
- day02【Collection、泛型】-笔记
day02[Collection.泛型] 主要内容 Collection集合 迭代器 增强for 泛型 教学目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合 ...
- java入门(Collection、泛型)
Collection.泛型 Collection 概述 集合就是容器,容器有数组,集合. 集合和数组既然都是容器,它们有什么区别呢? 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的 ...
- Java进阶知识笔记2【Collection、泛型】
[Collection.泛型] 主要内容 Collection集合 迭代器 增强for 泛型 学习目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合进行取元素 ...
- Collection,泛型
今日内容:[Collection,泛型] collection集合 迭代器 增强for循环 泛型 学习目标 能够说出集合和数组的区别 能够使用并说出Collection集合的常用功能 能够使用迭代器对 ...
- Collection、泛型
目录 一.集合 1.集合的介绍&集合和数组的区别 2.集合框架的介绍 二.集合的通用遍历方式--迭代器 1.集合迭代器的介绍和使用 1.获取集合的迭代器对象 2.遍历集合 2.迭代器的原理(画 ...
- 一、【Collection、泛型】
主要内容 Collection集合 迭代器 增强for 泛型 教学目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合进行取元素 能够说出集合的使用细节 能够使 ...
最新文章
- 'This NSPersistentStoreCoordinator has no persistent stores 报错
- 物联网时代传感器厂商竞争格局揭秘
- 文本域textarea 的值也是用value 来表示的。
- 自动化运维工具Puppet(管理资源)
- Linux_NetworkManager_RHEL7
- cambridge ccaf potential supervisors!
- Android鬼点子 100行代码,搞定柱状图!
- Android之HandlerThread源码分析和简单使用(主线程和子线程通信、子线程和子线程通信)
- 安装sql server 2000
- Moto XT1085 国行 解锁BL
- 将 cordova-simulate 依赖项安装到扩展时出错(解决方案)
- 通俗易懂的自动控制原理 # 绪论
- POI多个Sheet生成Excel
- recyclerView多条目加载,点击动画事件
- 自媒体是什么?三大媒体平台详细介绍,不了解的赶紧看着吧!
- OpenCV4.3 Java 编程入门:Core 组件中的数据结构与方法
- 一些常用的开发软件,定期更新!
- 设计模式----策略模式
- linux下做笔记的软件下载,Write一款梦幻般的Linux手机笔记应用程序
- python3爬虫豆瓣_Python爬虫(3)豆瓣登录