集合进阶
前言
接上期文章:JavaSE学习心得(API与算法篇)
教程链接:黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili
本期分享集合部分
单列集合
Collection
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的。
如果我们要往set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。如果当前要添加的元素已经存在,方法返回false,表示添加失败。因为set系列的集合不允许重复。
因为collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除。删除的元素不存在,就会删除失败。
contains底层是依赖equals方法进行判断是否存在的。如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在iavabean类中,一定要重写equals方法。
迭代器
直接看例子
报错NoSuchElementException
迭代器遍历完毕,指针不会复位
循环中只能用一次next方法
迭代器遍历时,不能用集合的方法进行增加或者删除
增强for遍历
其内部原理就是一个Iterator迭代器
修改增强for中的变量,不会改变集合中原本的数据。
还可以用lambda表达式:coll.forEach(s->System.out.println(s));
List
Collection的方法List都继承了 ,以下是List集合的特有方法
删除元素优先级
ArrayList
集合底层原理
利用空参创建的集合,在底层创建一个默认长度为0的数组
添加第一个元素时,底层会创建一个新的长度为10的数组
每次添加不够都乘1.5,还不够就用实际长度
数组查询快,增删慢
LinkedList
底层数据结构是双链表,查询慢,首尾操作的速度是极快的
泛型
直接看例子
泛型类
泛型方法
泛型接口
特性
泛型不具备继承性,但是数据具备继承性
?extends E:表示可以传递E或者E所有的子类类型
?super E:表示可以传递E或者E所有的父类类型
创建一个方法能养所有品种的猫,但是不能养狗
这些类之间的继承关系就不列出来了
二叉树
平衡二叉树
旋转策略:从添加的节点开始,不断的往父节点找不平衡的节点,观察这个节点是左边高还是右边高,接着看它高的那一边的子节点,看它这个子节点的左边高还是右边高,如果不平衡点和这个子节点较高的一边都在同一方向,那么就常规旋转,如果不是,先将这个子节点作为根节点的这个子树,旋转为和不平衡点的高低方向一致,然后再常规旋转。
举例子:
常规情况
高低方向不一致情况
红黑树
每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的
①根节点必须是黑色
②如果一个节点没有子节点或者父节点则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
③如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
④对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
红黑树在添加节点的时候,添加的节点默认是红色的
Set
实现类:
Hashset:无序、不重复、无索引
LinkedHashset:有序、不重复、无索引
TreeSet:可排序、不重复、无索引
Set集合打印也和List一样有三种方法,迭代器,增强for,lambda,但是打印出来是无序的
HashSet
底层原理
JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
HashSet遍历从0索引开始遍历各链表或者树,存就不一定了,所以存和取的顺序不一样
又是数组又是链表又是树的,用一个索引表示一堆元素不合适,所以没有索引
Hashset是利用什么机制保证数据去重的?Hashcode方法 equals方法
如果没有重写hashcode方法,不同对象计算出的哈希值是不同的如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
如果集合中存储的是自定义对象,必须要重写hashcode和equals方法,不是自定义的类,Java底层已经重写了那两个方法
LinkedHashset
底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序,效率低于Hashset
Treeset
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序。
对于字符、字符串类型:按照字符在ASCIl码表中的数字升序进行排序。
练习
Treeset的两种比较方式
方式一:
默认排序/自然排序:Javabean类实现Comparable接口指定比较规则
方式二:
比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则,相当于内部类
这里使用第一种
单例集合总结
1.如果想要集合中的元素可重复
●用ArrayList集合,基于数组的。 (用的最多)2.如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
●用LinkedList集合,基于链表的
3.如果想对集合中的元素去重
●用Hashset集合,基于哈希表的。 (用的最多)
4.如果想对集合中的元素去重,而且保证存取顺序
●用LinkedHashset集合,基于哈希表和双链表,效率低于Hashset。5.如果想对集合中的元素进行排序
用Treeset集合,基于红黑树。后续也可以用List集合实现排序
双列集合
Map
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null
在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
三种遍历方式
Map集合的第一种遍历方式
Map集合的第二种遍历方式
Map集合的第三种遍历方式
HashMap
HashMap是Map里面的一个实现类
没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
特点都是由键决定的:无序、不重复、无索引
HashMap跟HashSet底层原理是一样的,都是哈希表结构
依赖hashcode方法和equals方法保证键的唯一
如果键存储的是自定义对象,需要重写hashcode和equals方法
如果值存储自定义对象,不需要重写hashcode和equals方法
LinkedHashMap
相比于HashMap,多了一个有序,和LinkedHashset底层原理一样
TreeMap
跟TreeSet底层原理一样,可排序:对键排序,默认按照键的从小到大进行排序,也可以自己规定键的排序规则
自定义对象和TreeSet同理,需要自定义类去重写接口Comparable的compareTo方法
可变参数
直接看例子
1.在方法的形参中最多只能写一个可参数
2.在方法的形参当中,如果出了可变参数以外,还有其他的形参,那么可变参数要写在最后
Collections常用的API
例题
班级里有N个学生
要求:
70%的概率随机到男生
30%的概率随机到女生
班级里有5个学生
要求:
被点到的学生不会再被点到。
但是如果班级中所有的学生都点完了,需要重新开启第二轮点名。
需求
定义一个Map集合,键用表示省份名称province,值表示市city,但是市会有多个。
添加完毕后,遍历结果格式如下:
江苏省=南京市,扬州市,苏州市,无锡市,常州市湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市河北省=石家庄市,唐山市,邢台市,保定市,张家口市
斗地主游戏
准备牌
洗牌发牌
看牌
排序
不可变集合
在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合。
这个集合不能添加,不能删除,不能修改。
运用
创建Map的不可变集合
细节1:
键是不能重复的
细节2:
Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对
细节3:
如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法
这里两个张三会报错
这样也可以创建不可变集合,hm是已经添加数据的一个双列集合