2.8 Java之集合(Lish和Set)
集合概述
Java 集合也叫容器,util包下,用于存储对象,可分为 Collection 和 Map 两种体系
- Collection接口:
- Set:元素无序、不可重复的集合 —类似高中的“集合”
- List:元素有序,可重复的集合 —”动态”数组
- Map接口:具有映射关系“key-value对”的集合
类似于函数 y = f(x) (x1,y1) (x2,y2),每个点就是键值对
Collection接口继承树
- collection:接口,定义存储数据的功能,无直接实现类,定义了两个子接口
- collections:工具类
- 迭代器接口用于遍历集合里的元素
- ArrayList类底层也是数组实现的
- 主要6个实现类(SortedSet是接口)
Collection接口的方法
- 第一行利用多态性,创建Collection接口的实现类对象
- 123是包装类,添加方法的参数为对象
- add方法参数为对象,addAll方法参数为集合类对象
- Arrays为数组的工具类
- asList方法返回collection的子接口list,这是数组转化为集合的一种方法
- ArrayList类重写了ToString()方法,所以打印ArrayList,即打印其所有元素
- 引申:
- 要求集合里元素重写equals()方法(当元素的属性相同时,可认为是相同元素,而不是比较地址)
- 若没重写,根据object类的equals方法,即比较地址值
- containsAll方法即将集合元素每一个拿出来,进行contains比较,若出现一个则结果为false
- retainAll方法实质取交集,改变了主动调用的集合
- 哈希值决定存储位置,即栈空间通过该值指向堆空间区域
- 黄色波浪线:未使用泛型
- removeAll实质取两集合的差集,更改了主动调用的集合
- toArray转化的数组类型为Object类型,因为集合存储的元素也是Object类型
- 返回一个迭代器接口实现类的对象
- 采用法三,因为现成的方法,上方有个多态性语句(别忘)
迭代总结
- i看作指针,初始在第一个元素的上方
- i.next():将指针向下移动并返回值
错误示范
问题:间隔打印+最后元素的后面为空
增强for循环
- 冒号前:局部变量,集合中每个元素的类型
- 每次取一个元素赋值给局部变量,不改变原始数据
for循环和增强for循环
输出结果
原因:每次取一个元素赋值给局部变量,不改变原始数据
List接口
总览:
- Collection接口和Map接口并列
- Properties子类用于处理属性文件
ArrayList类
- 该类视作动态数组
- List有序,所以可在指定位置添加
- 打印list,元素位置是有序的
- add()方法是添加,不是覆盖对应位置元素,后方元素依次后退
- indexOf判断元素出现的位置,要重写equals方法,原因同以往章节
- 截取子list是左闭右开
- 打印list,元素有序性
LinkedList
- 适合频繁的增加和删除操作
- 不是数组结构,为链表结构
Vector
古老,处理速度缓慢,弃用吧
Set接口
Collection接口回顾
Set接口概述
- 可添加null
- 无序性!=随机性
- 无序性指的是存储空间的无序性,存储位置根据哈希值,哈希算法要求可保证元素的唯一性,而List是开辟一整块连续区域
- 哈希算法:不同元素的哈希值不同,类比坐位置,先hashcode找位置,没人直接坐,看有人占了座才调用equals方法比较
- HashSet重写了ToString方法,打印引用即打印集合元素
- 对于set:不可重复性要靠equals方法以及hashcode方法保证(缺一不可)
意味着自定义的类加入Set,要重写上述两方法 - 对于list:不可重复性只靠equals方法就行
- hashcode方法要求属性值一样,则hash值一样,反之,亦如此
即hashcode与equals的一致性 - hash值不同,直接存储,值相同,才调用equals方法比较
实例
能否加入Set集合,看hashcode方法,返回hash值不同,直接存储,值相同,才调用equals方法比较,属性又相同,才不存储
hashcode方法重写
- hashcode的写法采用提供的迭代写法,不采用注释里的写法,原因:
避免类似30+10=29+11的错误,违反了唯一性
LinkedHashSet
遍历顺序与添加顺序一致,因为链表维护添加顺序,并不能改变存储位置的无序性
原因如下
三部分:前向索引,后向索引,值,即双向链表
遍历快,利用索引如水银泻地
- 添加慢 要维护链表
TreeSet
- 类似元素为String,包装类排序按从小到大(根据重写的Comparable接口内的方法)
自然排序
重写Comparable接口的方法
- 上述方法若单单只比较一个属性,若两对象该属性一致,其他属性不一致,则后来的对象也不能添加到集合set中
- 可利用String类型或包装类已重写的comparable接口内的方法
- return 0很必要,不用抛异常的方法,return 0代表比较后元素的属性一致,即相同元素(实际可能未必),则不添加进set集合中
- compareTo方法在Set中优先级高于hashcode和equals方法,换句话说set集合看 compareTo方法来判断是否添加入集合中
测试类
- 要求set添加的对象为同一类(编译时不报错,运行时才报错),因为要比较属性
- 关注遍历使用(Object str : set),因为Set集合的元素默认是Object类型,而add方法的实参虽然是String,但形参是Object类型,没有使用泛型的锅
- person能否添加,全看 compareTo方法
定制排序
- 少写一个方法(Comparator接口还有equals方法),因为类对象的根父类Object已经重写了equals方法
- compare方法和上述compareTo方法内容一致,且都要求三位一体(注释解释)
简写如下(匿名内部类)
- compare方法比较的是customer,只能添加customer
- 每次添加对象时,自动找引用变量com对应的接口实现类的 compareTo方法
compare和compareTo辨析
- 一个写在添加的类里,一个在添加的类外
- 若添加的类可修改,则在类内部实现comparable接口
- 若添加的类无法修改,则在类外部利用Comparator接口指定排序
- 若两者都书写,最后定制排序优先于自然排序
TreeSet练习
MyDate类
- 书写属性注意顺序,影响自动生成的构造器
Employee类
自然排序
实现接口
Equals与Hashcode方法
MyDate类的两方法:
Employee类的两方法:
equals仍要书写,会与collection接口的部分方法对应,比如contains,还是要调用equals方法
- 先向mydate加入hashcode方法,再向employee添加hashcode
一个注意点
- 若employee类不重写compareTo方法,则一个对象都添加不了(JDK7.0后)
测试类
输出结果
- 不是按字母顺序,内部由hashcode排序,6次添加最后只加入5个元素,因为名字一样了(compareTo方法只看名字属性)
定制排序
- 定制排序优先
- 存入Set中的对象类型相同
- int类型成员变量直接相减来判断大小
- Set存储位置仍然是无序(hashcode决定存储位置)