JAVA12
集合框架
介绍
l 是一种容器可以用来存储多个数据
l 数组不可变 集合可变
l 集合中存储的是引用类型
ArrayList回顾
存储int类型
l 不接收基本类型但是java1.5后有自动装箱和自动拆箱能将int类型自动转换为包装类型
存储自定义类对象 引用类型
l 自定义类重写了Object类的toString方法
l 向集合中添加了对象后
l 遍历输出的集合里面的不再是对象内存地址 而是重写了的toString方法的格式输出
l toString 可以用工具直接生成 也可以自定义
集合学习目标
l 集合本身是一个存储的容器
l 必须使用集合存储对象
l 遍历集合 去除对象
l 集合自己的特性 ArrayList是一个可变数组
集合继承关系图
l 查询API
l 所有集合在Java.util包中
l 以ArrayList为例
- 继承了抽象类 实现了接口 List
l 关系图
- Set接口 集
- LinkedList 链表
- LinkedHashSet 基于链表的哈希表实现
- List 和Set 存储方式不一样
- 由于细节不同 共性相同 因此从共性的功能父类接口学习入手
Collection接口
概述
l 集合中的根接口 它定义的方法所有子类实现类可以使用
l 代表一组对象
l 一些允许重复的元素存在 List
l 一些不允许重复的元素存在 Set
l 一些是有序的 List
l 一些事无序的 Set
Collection接口中的方法
l add()
l clear()
- void clear()
- 清空集合中的所有元素
- 使用接口多态调用 新建集合对象
- 示例
- 有序集合
- 只是清空集合 不是销毁集合
l Contains()
- bollean contain(Object o)
- 判断任意对象是否存在于集合中
- 对象存在返回true
- 示例
- 如果
运行是false
应为123是integer “123”是String
l Size()
- 三种长度表现
数组 length 是属性 返回值是int
字符串 .length() 方法 返回值是int
集合 .size() 方法
l toArray
- 把集合中的元素转为为一个数组中的元素
- Object[] toArray()
- 返回值是一个存储对象的数组 存储的类型是Object
- 示例
- 数组不可变
- 意义
例如获取文件夹中的内容 获取需要集合 最终需要数组
l Remove()
- boolean remove(Object o)
- 如果有重复元素 只会删除一个
- 删除成功返回true 否则 false
iterator()迭代器接口
l 概述
- Java中有很多集合 集合容器不一样 存取方式不一样
- 为了方便统一存取 定义了迭代器
- 每一个集合一种通用的获取方式
- 为了进行遍历集合的
l 实现原理
娃娃机
l 抽象方法
- boolean hasNext()
- 如果仍有元素可以迭代,则返回 true。
- next()
- 返回迭代的下一个元素。
- 实现类
iterator 英 [ɪtə'reɪtə]
a) Collection有一个方法是
- Iterator
iterator() E是Collection中的类型参数
b) Collection 的方法是实现了接口 Iterable 的Iterator
c) 这个方法返回的是一个在此 collection 的元素上进行迭代的迭代器。也就是一个Iterator接口的实现类的对象
d) 然后就可以用这个实现类的对象引用进行调用方法
e) 也就可以对集合进行遍历或者说叫迭代
f) 示例
通用遍历模式 hash也可以
l 执行过程
- 调用iterator方法运行后集合内部就会产生一个指针
- 指针指向-1位
- While循环 hasNext判断有没有元素
- 有元素 就执行 Next方法 指针移动指向这一个元素
- 直到下一个没有元素 指针指向 空 返回false
- 不写next 指针不会动 会进入死循环
- 如果循环完毕 再次执行 next 会报没有元素异常
- 循环完毕指针不会返回 所以迭代器只能执行一次
- For循环迭代
节省内存
集合迭代中的转型
l 集合不指定存储的数据类型
l 集合什么都能存
l 迭代器获取 也不能加类型
l next取出的是Object类型
l 输出的也是Object类型
l 如果需要输出不同类型的特有属性就Object需要向下转型 在调用
增强For循环 JDK1.5后的新特性
l 引入
- 出现了一个新接口Iterable
- Collection不在是总的接口
- 在java.lang包中
- 实现一个增强的for循环
- 实现这个接口允许对象成为foreach语句的目标
- 数组 和所有集合都可以使用增强for
l 格式
a) i相当于只是可以进行赋值的指针
b) 只是轮换赋值和数组中的元素没有关系
- 优点
a) 代码少了 方便对容器进行遍历
- 缺点
a) 没有索引不能操作容器里面的元素
l 练习
- 遍历集合 集合存储引用类型
- 只做遍历 可以用 要是需要操作数据比如 排序 查找 等就不行
- 可以进行调用引用类型的方法
泛型
引入
l 示例
- 不写泛型的集合
- 输出字符串长度需要强转
此时在输入其他类型比如 1 integer类型
输出就会报异常
出现安全问题
所以引入了泛型
定义和使用
l Jdk1.5 出现安全机制 保证程序的安全性
l 指明了集合中的存储数据的类型<数据类型>
l 解决了安全问题
l 不需要强转 减少了代码量
l 可以使用增强for
伪泛型
l 只是编译手段
l 符合类型编译失败
l 不符合类型编译成功
l 编译后的文件里没有泛型
l 真泛型 c++
带有泛型的类
l 例如ArrayList类
- java.util
类 ArrayList 就是泛型 - Element 元素 实际思想就是一个变量
- E写数据类型 String Integer
l
泛型的方法
l add()
- 带泛型的方法
- boolean add(E e)
- E跟着类的E同步
l
toArray(T[] a)
- 传递的数组类型和类的E相同
泛型的接口
l java.util
接口 List
- 实现类先实现接口 不理会泛型
l 实现类实现接口的同时指定了数据类型
泛型的优点
l 保证了在强行转换时安全问题
l 避免了强转
l 把错误由运行提前到了编译
l 带来了增强For
l 必须是jdk1.5后
l Jdk1.4没有泛型 只能靠程序员自己解决
泛型的通配符
l Dos通配符 del s.* *就是通配符
l 同时迭代两个集合
l 参数类型应该写共同实现的接口或父类
数据类型泛型应该写 通配符 ?
l 迭代器数据类型也应该写 ?
泛型的限定
l
l
List
特点
l 有序集合
l 取出时的顺序和存进去的顺序一样
l 具有索引 能精准控制位置
l 允许存储重复元素
l 实现类 ArrayList满足以上特点
抽象方法
l List带索引的方法是特有方法
l Boolean add(E e)
- 向列表尾部添加指定的元素
- 和collection接口一样
l void add(int Index,E)
- 将元素插入到列表的指定索引上
- 无返回值
- Java.awt.List
- Java.util.List 是我们学习的包
- 不能超过两位集合索引进行添加 可以超过一位集合索引进行添加
- 否则会索引越界异常
分别是索引越界异常
数组越界异常
字符串越界异常
l E remove(int index)
- 移除指定索引的元素
- 不能超过索引删除
- 返回删除之前的元素
l Boolean remove(object)
- 返回布尔值
- 从列表中移除第一次出现的指定元素
- 和父接口Collection一样
l E Set(int index, E )
- 修改指定索引的上的元素
- 返回修改之前的元素
- 不能越界修改
迭代器的并发异常
l 不能在用迭代器遍历集合时,修改集合的长度
List存储数据的结构
l List下有很多集合,他们所采用的结构方式是不同的,这样就导致了集合有各自的特点
供我们在不同的情况下使用
l 存储形式 常见得有 堆栈 数组 队列 链表 数组链表比较多 因为查询 修改比较多
l 堆栈
- 先进后出
- 手枪子弹仓
l 数组
- 查找速度快
- 连续的索引
- 增删速度慢
l 队列
- 先进先出
- 排队过安检
l 链表
- 增加删除速度快
- 查找速度慢
- 多个节点地址连接
- 自行车链条
ArrayList
自身特点
l 大小可变的数组实现
l 次实现不同步 线程不安全 执行速度快
l 里面有声明了一个Object数组elementData 什么都能存
l 在new ArrayList时构造方法可以指定数组的长度
l Add 添加吧添加的元素放到数组里,并且调用一个方法用copyof进行扩容
l 空参时时默认10个元素大小的数组
l 因为是数组所以如果有查找需求就用ArrayList
LinedList
自身特点
l List接口的链表实现
l 单向链表
l 增加删除速度快
l 大量首尾操作
特有方法
l 链表底层实现
l addFirst(E) addLast(E)
- 添加到链表的开头
- 添加到链表的结尾
- 示例
a) addLast(E)放到第一个语句 相当于add
b) 先写Last 后写 First 还是是先是First
l E getFirst() Egetlast()
- 获取链表的开头和结尾
- 返回集合类型的一个元素
- 如果集合为空 就会没有元素异常
- 因此需要判断一下列表的size是否为0
- 或者判断一下is.Empty() 这个方法源码是判断了一下集合是否等于0
- Is.Empty()是父类Collection接口的方法
l E removeFirst() E removeLast()
- 移除并返回移除的元素
Vecotor集合
特点
l 存储结构是数组
l JDK当中最早的集合 JDK1.0
l 从JDK v1.2改进实现List 接口
l 是同步 线程安全 运行速度慢
l 已被ArrayList取代
l 和ArrayList方法差不多
l 区别
- 方法
- addElements 相当于add
- elements 返回此向的枚举 相当于跌倒器
- has和next相当一迭代器的has和next
- 1.2出现后
Set接口
介绍
l 不允许存储重复元素
l 取元素只能采用 迭代器和增强For
l 不能使用索引相关的方法
l 方法和Collection一样
HashSet
介绍
l 此类实现了set接口
l 有哈希表支持
l 实现实际是一个Map集合
l 构造器new 的时候实际上是new了一个map集合
l 取元素不保证存的顺序 无序集合
l 不保证顺序永远不变
l 不存储重复元素
存储和迭代
l 和ArrayList代码编写完全一致
l 示例
- 迭代器
- 增强For
- 不存储重复元素
哈希表的数据结构
l 存储取出比较快
l 线程不安全 运行速度快
l 哈希表的解释图
- 链表数组结合体
- 数组存储的是一个个链表
- 桶比喻链表的数量
- 初始容量表示数组的容量
- 加载因子是数组长度的百分比
- 初始容量默认是16
- 加载因子默认是0.75 16*0.75
- 初始容量和加载因子都可以设置
- 当桶占数组的12个时 数组会扩容一倍 即数组复制
- 数组复制很难占用内存资源
- 加载因子太大 需要扩容时太慢
- 加载因子太大 会频繁扩容
- 数组扩容叫做数组的再哈希
字符串对象的哈希值
l 来源于父类Object
l 对象的哈希值是十进制整数
l HashCode所有类都可以有
l 是个本地方法没有源码
l 哈希值不确定
l 可以被子类重写
l 存储在HashSet的依据
l String重写了HashCode方法
- hash是String源码中定义的默认是0
- 因此hashcode和字符串每个元素的Ascii值有关 因此同一个字符串hashcode一样
哈希表的存储过程
l 可以存储null
l 代码
l 图
- 先调用hashCode查询对象哈希值
- 如果没有这个哈希值就存储这个对象
- 如果又来一个 再次调用hashCode查询对象的哈希值
- 这个哈希值和上一个一样
- 此时会调用后来的对象调用equals方法和第一个进行比较
- 结果为true 并且两个对象哈希值一样
- 因此哈希表判断出这个元素已经重复
- 如果前5部一样,第6步的结果是false
- 会采用桶的方式进行存储
- 源码在HashMap put方法中
哈希表存储自定义对象
l 可以存储属性值相同的两个不同对象
l 为避免重复需要自定义对象重写hashCode和equals方法
l 参考String的算法
l name.hashCode()+age
l 姓名年龄不同hashcode相同
l 因此还需要重写equals
l hashCode巧合相同太多 总调用equals效率太低
l 因此需要修改hashcode方法 修改该会降低hashcode相同的概率
不要乘1和零其他的都会降低概率
l 但不是绝对的 String乘以31还是有
l 可以alt shift s可以自动生成
LinkedHashSet
介绍
l 具有可预知迭代顺序
l 具有顺序存储和取出的顺序一样
l 双向链表
l 线程不安全的集合运行速度快
l 不允许重复
ArrayList和HashSet判断对象是否重复
ArrayList是否重复
l 用父类contains方法进行判断
l 该方法依赖于equals方法
l
HashSet是否重复
l Add
l Contains 依赖于equals
其他
l 只要放入集合就要考虑重写equals和hashCode
l 八种数据类型和基本类型包装类型都重写了这两个方法
l 避面重复性
面试题
两个对象
哈希值相同
equals返回值一定是true吗
正确答案:不一定
两个对象
如果equals方法返回true
哈希值一定相同吗
正确答案:一定
初步分析
l 题目一
- 重写了两个方法
- 证明了题目一 答案是不一定的
l 题目二
- 重写了方法
- 证明了题目二的答案也是不一定的
进一步分析
l 在Object类的hashCode 中Sun公司对此有规定
l 返回哈希码值能有效的避免哈希表内的属性相同的重复对象元素,从而降低哈希表的运算和存储压力,提高了哈希表的性能
l 返回的哈希值必须一定 否则会违反第一项协定 比如题目二的更改后的 a+1
l 如果根据equals判定两个对象相等 哈希值必须一样 所以题目二 答案是正确的
l 如果两个对象不相等 哈希值不一定是真
l Equals也有类似规定