概述
集合
数组和集合的元素存储个数问题
数组定义后类型确定,长度固定 集合类型可以不固定,大小是可变的
数组和集合存储元素的类型问题
数组可以存储基本类型和引用类型的数据 集合只能存储引用类型的数据
数组和集合适合的场景
数组适合做数据个数和类型确定的场景 集合适合做数据个数不确定,且要做增删元素的场景
Collection单列集合(一个元素对应一个值)
List系列集合 添加的元素是有序、可重复、有索引 ArrayList、LinekdList:有序、可重复、有索引
Set系列集合 添加的元素是无序、不重复、无索引
HashSet:无序、不重复、无索引 LinkedHashSet:有序、不重复、无索引
TreeSet:按照大小默认升序排序、不重复、无索引
实例
集合对于泛型的支持
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
注意 集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象
如果集合中要存储基本类型,要使用包装类 里面存储的45 12 也已经自动装箱,不再是基本数据类型
Collection 集合常用API
先学习的原因 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() 把集合中的元素,存储到数组中 我们一般定义一个Object[]的数组,因为集合中的元素可能有很多类型(见上图)
Collection集合遍历方法
方法一:迭代器(Iterator),迭代器是集合的专用的遍历方式
Collection集合获取迭代器
Iterator<E> iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
快捷键:list.iterator() ctrl+alt+v
Iterator常用方法
boolean hasNext() 询问当前位置是否有元素存在,存在返回true,不存在返回false
E next() 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界
方法二:增强for循环
既可以遍历集合,也可以遍历数组 其内部原理相当于Iterator迭代器,
实现Iterable 接口的类才可用迭代器和增强for,Collection接口已经实现了Iterable接口
格式 for(元素数据类型 变量名 :数组或者Collection集合){
//在此处使用变量名即可,该变量就是元素
} 快捷键 list.for
方法三:Lambda表达式遍历集合
Collection结合Lambda遍历API
default void forEach(Consumer<? super T> action) 结合lambda遍历集合
输入list.forEach(new )
到这就会有提示,直接选上就ok
存储自定义类型的对象(自定义了一个Movie类)
右图是内存分析
栈内存中的movies存储的是集合的地址,集合存储地址指向电影对象,增强for遍历通过mo变量来根据地址找内容
常见数据结构
栈
数据结构的执行特点 后进先出,先进后出
数据进入栈模型的过程为:压/进栈 数据离开栈模型的过程为:弹/出栈
队列 数组
先进先出,后进后出 数组是一种查询快,增删慢的模型
查询速度快:查询数据通过地址值和
索引定位,查询任意数据耗时相同。
删除效率低:要将原始数据删除,同
时后面每个数据前移
添加效率极低:添加位置后的每个数据后移,再添加元素
链表
链表的元素是游离存储的,每个元素节点包含数据值和下一个元素的地址
链表查询慢,无论查询那哪个数据都要从头开始找
链表增删相对快
二叉树
只能有一个根节点 每个节点最多支持2个直接子节点
节点的度 节点拥有的子树的个数,二叉树的度不大于2,叶子节点 度为0的节点,也称之为终端节点
高度 叶子节点的高度为1,叶子节点的父节点高度为2,以此类推,根节点的高度最高
层 根节点在第一层,以此类推
兄弟节点 拥有共同父节点的节点互称为兄弟节点
二叉查找树又称二叉排序树或二叉搜索树
特点 每个节点最多有两个子节点
左子树上所有节点的值小于根结点的值
右子树上所有节点的值大于根结点的值
其实和二分查找相似,目的是提高检索性能
平衡二叉树 任意节点的左右两个字树的高度差不超过1,任意节点的左右两个字数都是一颗平衡二叉树
红黑树 红黑树是一种自平衡的二叉查找树,它的平衡不是通过高度平衡的,而是通过"红黑规则”实现的
规则 每一个节点或是红色,或是黑色,根节点必须是黑色 如果一个节点没有子节点或者父节点,则该节点相应的指针属性为Nil ,这些Nil视为叶节点,叶节点是黑色的
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
对于每一个节点,从节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
List集合特有方法(List支持索引,所以多了很多索引操作的独特api,其它Collection的功能List也都继承了)
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove (int index) 删除指定索引处的元素,返回被删元素
E set (int index,E element) 修改指定索引处的元素,返回被修改元素
E get (int index) 返回指定索引处的元素
List集合遍历
可以用Collection的三种+自己独有的for索引遍历,因为有索引
ArrayList集合底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要元素的移位操作
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组,当长度不够时,会以1.5倍的数组长度扩容
LinkedList集合底层原理
底层数据结构是双链表(既能代表栈,也能代表队列),查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素
并发修改异常
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题,例如当我们要删除的元素有两个或多个时,系统会判定异常,因为会出现漏删的情况
那些遍历存在问题 迭代器遍历集合且直接用集合删除元素的时候出现(见下图1注释部分) 增强for循环遍历集合且直接用集合删除元素时出现(Lambda也是如此,底层也是增强for)(原因是在删除元素时后面的元素会补上,然后索引值又会+1,导致删除不完全)
哪种遍历且删除元素不出问题 迭代器遍历集合但是用迭代器自己的删除方法操作可以解决(见下图1)
使用for循环遍历并删除元素不会存在这个问题
也会漏掉元素,以前我们解决这个问题是 从后面开始删 或 加 i--
泛型
可以在编译阶段约束操作的数据类型,并进行检查 格式 <数据类型> 泛型只能支持引用数据类型
集合体系的全部接口和实现类都是支持泛型使用的
好处 统一数据类型 把运行期间的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来
自定义泛型类
定义类的同时定义了泛型的类就是泛型类 泛型类的格式:修饰符 class 类名 <泛型变量>{ }
范例 public class MyArrayList<T>{ } 此处的T可以写成任意标识 如E T K V
作用 编译阶段可以指定数据类型,类似于集合的作用
自定义泛方法
定义方法的同时定义了泛型的方法就是泛型方法
泛型方法的格式:修饰符<泛型变量> 方法返回值 方法名称 (形参列表){ }
范例:public <T> void show(T t){ } 方法中可以使用泛型接受一切实际类型的参数,使方法更具备通用性
核心思想 把出现泛型变量的地方全部替换成传输真实数据类型
作用 方法中的一切实际类型的参数,方法更具备通用性
自定义泛型接口
使用泛型定义的接口就是泛型接口
格式 修饰符 interface 接口名称 <泛型变量>{} public interface Data<E>{}
作用 泛型接口可以让实现类选择当前功能需要操作的数据类型
实现原理 实现类可以在实现接口的时候传入自己的操作数据类型,这样重写的方法都将是针对于该类型的操作
接口实现类(泛型是Teacher类(前提是有Teacher这个类),那么重写的方法都是针对于该类型的操作)
通配符: ?
?可以在"使用泛型"的时候代表一切类型 E T K V是在定义泛型的时候使用的
案例 一个极品飞车游戏 所有汽车都可以参加比赛
但是会出现问题,当我们定义一个Dog类的对象,也可以传进去
这样就会出现问题,所以我们引出 泛型的上下限概念
? extends Car : ?必须是Car 或者其子类 泛型上限
? super Car: ?必须是Car 或者其父类 泛型下限(如下图,Dog不是Car的子类,所以出错)
Set系列集合特点
无序 不重复 无索引
Set实现类特点
HashSet 无序 不重复 无索引 LinkedHashSet 有序 不重复 无索引 TreeSet 排序 不重复 无索引
HashSet底层原理
HashSet集合底层采取哈希表存储的数据 哈希表是一种对于增删改查数据性能都较好的结构
哈希表的组成
JDK8之前 底层是数组+链表 JDK8之后 数组+链表+红黑树
哈希值
是JDK根据对象的地址,按照某种规则算出来的int类型的数值
Object类的API
public int hashCode():返回对象的哈希值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的 默认情况下,不同对象的哈希值是不同的
HashSet1.7版本 数组+链表+(结合哈希算法)
创建一个默认长度为16的数组,数组名为table
根据元素的哈希值跟数组的长度求余计算出应存入的位置
判断当前是否为null,如果是null,直接存入,不为null,表示有元素,则调用equals方法比较,如果一样,不存,不一样,存入数组
JDK7新元素占老元素位置,指向老元素,JDK8新元素挂在老元素下面
JDK1.8版本开始HashSet原理解析
底层结构 哈希表(数组、链表、红黑树的结合体)
当挂在元素下面的数据过多时,查询性能降低,从JDK8开始,当链表长度超过8时,自动转换为红黑树
哈希表的详细流程
创建一个默认长度为16,默认加载因子为0.75的数组,数组名table
根据元素的哈希值跟数组的长度计算出应入的位置
判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性,如果一样,则不存,如果不一样,则存入数组
当数组存满到16*0.75时,就自动扩容,每次扩容原先的两倍
如果希望Set集合认为2个内容相同的对象是重复的应该怎么办
重写对象的hashCode和equals方法(Set集合去重复的原理是 先去判断哈希值,再去判断equals)
在自己定义的Student类中,alt+insert 选择要重写的
LinkedHashSet集合特点概述和特点
有序(是指保证存储和取出的元素顺序一致) 不重复 无索引
原理 底层数据结构依然是哈希表 只是每个元素有额外多了一个双链表的机制记录存储的顺序
TreeSet集合默认的规则
对于数值类型:Integer,Double,官方默认按照大小进行升序排序
对于字符串类型:默认按照首字符的编号升序排序
对于自定义类型如Student对象,TreeSet无法直接排序
自定义排序规则
TreeSet集合存储对象的时候有2种方式可以设计自定义比较规则
方式1 让自定义类实现 Comparable 接口 重写里面的compare To 方法来制定比较规则
方式2 TreeSet 集合有参数构造器,可以设置Comparator接口对应的比较器对象,来指定比较规则
可变参数
可变参数用在形参中可以接收多个数据
可变参数的格式 : 数据类型...参数名称
作用 传输参数非常灵活,方便。可以不传输参数,也可以传输不定个数的内容,也可以传输一个数组
可变参数在方法内部本质上就是一个数组
注意事项 一个形参列表中可变参数只能有一个 可变参数必须放在形参列表后满
Collections集合工具类
Collections并不属于集合,是用来操作集合的工具类
常用API
public static <T> boolean addAll(Collection<? super T> c ,T...Elements) 给集合对象批量添加元素
public static void shuffle(List<?> list) 打乱List集合元素的顺序
Collections排序相关API
适用范围 只对于List 集合 Set集合是根据自己的哈希值来定位的
排序方式1 public static <T> void sort(List<T> list) 将集合中的元素按照默认规则排序
本方式不可以直接对自定义的类型List集合排序,除非自定义类型实现了比较规则Comparable 接口
排序方式2 public static <T> void sort(List<T> list,Comparator<? super T> c ) 将集合中的元素按照指定那个规则排序
按照价格,由于价格是double类型的,而我们定义的是int类型的,所以用类型的Double
Map集合概述和使用
Map集合是一种双列集合,每个元素包含两个数据 Map集合的每个元素的格式:key=value(键值对元素) Map集合也被称为"键值对集合"
格式
Collection结合的格式 [元素1,元素2,元素3...]
Map集合格式{key1=value1,key2=value2,key3=value3,...}
Map集合体系
特点
Map集合特点是由键决定的
Map集合的见是无序,不重复,无索引,值不做要求(可以重复)
Map集合后面重复的键对应的值会覆盖前面重复键的值
Map集合的键值对都可以为null
Map集合实现类特点
HashMap : 元素按照 键 是无序,不重复,无索引,值 不做要求
LinkedHashMap:元素按照 键 是有序,不重复,无索引,值 不做要求
TreeMap: 元素按照 键 是排序,不重复,无索引,值 不做要求
Map集合
Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的
常用API
V put(V key,V value) 添加元素
V remove(Object key) 根据键值对删除元素
void clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containValue(Object value) 判断集合是否包含指定的值
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中键值对的个数
Map集合的遍历方式
1 键找值
先获取Map集合的全部键的Set集合 遍历键的Set集合,然后通过键提取对应值
设计的API Set<K> keySet()获取所有键的集合 V get(Object key)根据键获取值
2 键值对
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了 先遍历Set集合,然后提取键以及提取值
涉及API
Set<Map.Entry<K,V>>entrySet() 获取所有键值对对象的集合
K getKey() 获取键
V getValue() 获取值
3 Lambda
Map结合Lambda遍历的API
default void forEach(BiConsumer<? super K,? super V> action)
Map实现类
HashMap的特点和底层原理(其实Set底层就是Map,只不过只有键,没有值罢了)
由键决定 无序、不重复、无索引 HashMap底层是哈希表结构
以 hashcode方法和equals 方法保证键的唯一
如果键要存储的是自定义对象,要重写hashCode和equals方法
LinkedHashMap集合概述和特点
由键决定:有序 不重复 无索引
这里的有序指的是保证存储和取出的元素顺序一致
原理 底层数据结构依然是哈希表,只是每个键值对元素有额外多了一个双链表的机制记录存储的顺序
TreeMap集合概述和特点
由键决定特性 可排序 不重复 无索引
可排序 按照键数据的大小默认升序排序,只对键排序,也可以自己定义比较规则 TreeSet时讲过,当我们选择用谁来比较是,就以谁为一样做相同的判断标志 例如用weight,如果两个weight一样,那就视为重复
不可变集合
不可变集合,就是不可被修改的集合 集合的数据项在创建的时候提供,并且在整个声明周期中都不可改变
为什么要创建不可变集合
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践
或者当集合中对象被不可信的库调用时,不可变形式是安全的
如何创建
在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
static <E> List<E> of(E...elements) 创建List集合对象
static <E> Set<E> of(E...elements) 创建Set集合对象
static <K,V> Map<K,V> of(E...elements) 创建Map集合对象
最后
以上就是暴躁哑铃为你收集整理的Java进阶2(集合+泛型)的全部内容,希望文章能够帮你解决Java进阶2(集合+泛型)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复