集合框架List_Set
1.内容介绍
集合框架的学习方式(了解)
ArrayList(掌握)
LinkedList(掌握)
HashSet(掌握)
TreeSet(掌握)
Collection体系(了解)
2.什么是集合框架List_Set
1.昨天我们认识了数据结构,也自定义了数据结构,用于数据的增删改查等等,其实这种集合框架Java已经为我们写好了,List和Set都是Java已经写好的数据结构
2.List和Set是接口:我们主要用接口下面实现类
3.来认识下集合体系,请看下图
3.集合框架的学习方式
多查询API
本阶段会学习:常见的Java中写好的集合框架的类和接口; 主要对数据做 增删改查
如何创建容器对象;=>构造方法 方法
如何把数据装到容器中—》 调用方法;
如何把容器中的数据取出—>还是调用方法;
可能的难点:我们会学习各种各样的集合容器类,它们有各自的存储特点,难以选择
解决:分析清楚每一个的特点,实际场景中自己的需求,如果很多都可以,那就随便选
在本阶段任务要求:
学会使用
了解我们提到的Java中的这些容器类的设计原理
最近看到类型后面有 直接忽略,过几天学习的泛型;直接看成Object
最近看到应该是数据类型的地方出现了 E T K V… 统统看成是Object,讲泛型的时候讲解
集合框架中存储的元素全部都是对象,从1.5开始支持自动装箱拆箱,所以基本数据类型也可以直接添加
4.ArrayList
4.1.ArrayList介绍
内部基于数组实现的一个集合类。查询比较快,添加和删除相对比较慢
猜ArrayList中会有哪些功能(方法): 添加、删除、查询、插入、修改。。。
不是同步的(存在线程安全问题),如何解决:敬请期待…
4.2.基本使用
4.3.遍历方式
1.使用普通的for循环
2.增强for循环(foreach)
3.使用迭代器进行遍历
Iterator 就是一个迭代器(也是一个接口)其中的方法如下:
boolean hasNext() 判断是否有下一个元素,如果返回true表示有下一个;
Object next() 调用一次获得一个元素(每调用一次指针会向后移动一个);
void remove() 会从迭代器指向的结合容器中删除一个元素
ArrayList bag = new ArrayList();
bag.add("电脑");
bag.add(200);
bag.add("鼠标");
bag.add("小人书");
bag.add("教材");
bag.add("牛奶");
for(int i=0;i<bag.size();i++){
System.out.println(bag.get(i));
}
//使用迭代器遍历集合ArrayList bag获得一个迭代器
Iterator it = bag.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
4.4.迭代器注意事项
1.如下结果:只遍历了一次,为什么?
Iterator it = bag.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
while(it.hasNext()){
System.out.println(it.next());
}
原因:上面两个while使用的是同一个迭代器,第一个while循环完毕就把指针移动到末尾了,所以第二个while不会执行了:如下示意图
2.双向迭代器
Iterator 单项的迭代器接口,从左到右依次获得数据,判断是否有下一个;获得下一个
|-- ListIterator 双向的迭代器接口,它的主要方法如下:
Iterator中有的它也有;
boolean hasPrevious() 判断是否有上一个;
Object previous() 获得上一个元素;
代码清单:
ArrayList bag = new ArrayList();
bag.add("电脑");
bag.add(200);
bag.add("鼠标");
bag.add("小人书");
bag.add("教材");
bag.add("牛奶");
// 获得双向的迭代器
ListIterator iterator = bag.listIterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("-----------------");
while(iterator.hasPrevious()){
System.out.println(iterator.previous());
}
4.5.小结
5.LinkedList
5.1.ListList介绍
1、 内部是基于链表结构实现的。添加和删除比较快,查询相对ArrayList比较慢
2、 内部相对于ArrayList而言多了一些操作头和尾的方法
3、 可以充当队列,堆栈
4、 不是线程安全的(同步的)
5.2.基本使用
5.3.小结
6.自定义不重复元素的容器
6.1.如何自定义
1.现在需要我们自己设计一个容器类,不能够添加重复元素(如果元素重复了就添加不进去)
2.在前面我们设计的基于数组的自定义容器类的基础上改版
3.需要在add方法内部判断重复
4.如何判断:
(1)每一次调用add会传入一个参数: 用户希望添加的元素 ele
(2)遍历内部的数组,判断数组中是否包含ele
6.2.代码实现
public boolean add(Object ele){
for (Object object : data) {
if(ele.equals(object)){
return false;
}
}
if(size == data.length){//扩容[ +10 ]新数组 将久数组值,复制到新数组
Object[] newArr = new Object[size+20];
System.arraycopy(data, 0, newArr, 0, size);
data = newArr;
}
data[size] = ele;
size++;
return true;
}
6.3.小结
7.HashSet
7.1.HashSet介绍
1.不能够添加重复元素
2.无序(不保证和添加和打印顺序一致)
3.初体验
HashSet hs = new HashSet();
hs.add("A");
hs.add("D");
hs.add("C");
hs.add("B");
hs.add("B");
System.out.println(hs.size());
System.out.println(hs);
7.2.如何判断重复的
1.通过添加进去的元素的hashCode+eqauls 两者进行比较
2.如果两个对象的hashCode相等 并且 两个对象调用equals结果是true 才认为两个元素重复
7.3.验证上面的规则
1.打印上面示例中的元素的hashCode和equals的结果
2.尝试自定义类,覆写hashCode 和 equals 这两个方法中的代码随便写
(1)尝试修改两个方法的值来测试容器中添加了几个对象
class Student{
String name;
int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return new Random().nextInt();//随机值,出现不同值的可能性很大
}
@Override
public boolean equals(Object obj) {
return true;//对象比较结果为true
}
@Override
public String toString(){
return name+"-"+age
}
}
1.测试代码
HashSet hs = new HashSet();
//地址值 对象属性是否一致{现实}
Student stu1 = new Student("OOO",18);
Student stu2 = new Student("OOO",18);
//stu1 stu2 HashCode值是否一致 equals比较的结果,决定是否是同一个对象,决定能否添加到hs
hs.add(stu1);
hs.add(stu2);
System.out.println(hs.size());
System.out.println(hs);
7.4.实际开发情况分析(重点)
1.覆写的时候应该参考实际业务中的比较规则,例如姓名,年龄等,根据对象属性判断对象是否重复
@Override
public int hashCode() {
return name.hashCode()+age;//由属性来决定对象的hash值
}
@Override
public boolean equals(Object obj) {
Student stu = (Student)obj;
return this.name.equals(stu.name)&&this.age == stu.age;
}
2.自动覆写HashCode及Equals方法参考学习Java怎么写的
hashCode
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
(1)hashCode自动覆写分析
equals
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
(2)equals自动覆写分析
7.5.小结
8.TreeSet
8.1.TreeSet介绍
1.无序:不保证(不记录)我们的添加顺序;
2.不重复:不能够添加重复元素(多个心眼)如何判断重复的呢?
3.感觉内部存储有一定的顺序
a)注意:TreeSet一旦添加了第一个元素后就不能添加其它数据类型的元素了,只能添加相同数据类型的元素,除非将容器中所有的元素全部清空,才能添加新的数据类型的元素
8.2.简单体验
体验1:不重复及不保证添加顺序和打印顺序一致
TreeSet ts = new TreeSet();
ts.add("B");
ts.add("A");
ts.add("C");
ts.add("C");
System.out.println(ts.size());
System.out.println(ts);
1.结果: 虽然打印结果的顺序和添加顺序可能不一致,但是感觉结果是有某种规则排序的
2.说明String类也实现了Comparable接口,String对象可以调用compareTo方法
体验2:添加了一种类型不能在添加其他类型
TreeSet ts = new TreeSet();
ts.add("B");
ts.add("A");
ts.add("C");
ts.add("C");
ts.add(10);
System.out.println(ts.size());
System.out.println(ts);
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at java.lang.Integer.compareTo(Unknown Source)
at java.util.TreeMap.put(Unknown Source)
at java.util.TreeSet.add(Unknown Source)
at cn.itsource._07TreeSet.TestTreeSet.main(TestTreeSet.java:12)
1.结果:居然不能够放不同的类型,但是编译没有错
体验3: 添加自定义的类的对象 Student对象
TreeSet ts = new TreeSet();
Student stu1 = new Student("王者",18);
ts.add(stu1);
System.out.println(ts.size());
System.out.println(ts);
Exception in thread "main" java.lang.ClassCastException: cn.itsource._07TreeSet.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(Unknown Source)
at java.util.TreeMap.put(Unknown Source)
at java.util.TreeSet.add(Unknown Source)
at cn.itsource._07TreeSet.TestTreeSet.main(TestTreeSet.java:9)
1.结果:
2.疑问1:上面的代码添加的都是同种类型的数据,为什么还报错;
3.疑问2:为什么提示要把Student转成Comparable
4.正常情况 ----> TreeSet 或者 Comparable的文档
8.3.TreeSet的结构(存储原理)分析
1.TreeSet内部是按照大小进行排序的,大小有对象与对象之间比较进行决定的
2.设计TreeSet之前:Java设计了一个接口Comparable接口,其中提供了对象之间比较的方法CompareTo
3.TreeSet会调用对象的CompareTo方法,比较对象,所以我们放入的对象需实现Comparable
8.4.自然排序与定制排序(比较器)
8.4.1.自然排序Comparable
什么是自然排序
1.例如:大家排队,按照高矮个排队,那么同学们自己相互之间就能进行对比,每个对象具备自我比较判断的能力,称之为自然排序
Comparable中API文档中的描述:
2.此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法
理解:
3.如果一个类实现了Comparable接口,可以认为这个类的对象具有自然排序的能力(本质就是这个对象可以调用比较的方法compareTo),这种比较和排序的规则就是自然排序
@Override
public int compareTo(Object o) {
if(o==null){
return 0;
}
Student stu = (Student)o;
if(this.age>stu.age){//如果大于的时候返回负数表示降序,返回负数表示升序
return -1;
}else if(this.age<stu.age){//如果大于的时候返回负数表示降序,返回负数表示升序
return 1;
}else{//如果年龄不大于也不小于说明等于,那么有名字来决定对象是否相同String已经写好排序规则
return this.name.compareTo(stu.name);
}
}
测试代码
TreeSet ts = new TreeSet();
Student stu1 = new Student("XXX",20);
Student stu2 = new Student("XXX",20);
ts.add(stu1);
ts.add(stu2);
System.out.println(ts.size());
System.out.println(ts);
1
[Student [name=XXX, age=20]]
自然排序小结
8.4.2.定制排序(比较器)Comparator
什么是定制排序
定制排序,相当于给容器提供了一个裁判:例如操场就是一个容器,那么体育老师就是这个裁判,每个同学进入操场同学们相互之间可以比较谁比较帅,或者不可以比较谁帅都可以,有了老师这个裁判,老师可以负责比较每一位同学
为什么需要定制排序
原来的比较规则不符合所有人的使用需求
不存在比较规则的对象,无法放入容器,我可以给容器提供定制比较器
定制排序如何实现
Comparator 是一个比较器的接口(标准),必须得有进行比较的方法 :compare(Object o1,Object o2);
自定义一个类实现Comparator接口,其中写比较规则 —> 比较器的模板,我们现在需要的是一个具体的比较器对象
public class MyComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
if(o1==null||o2==null){//判断,若两个有一个为null都直接返回
return 0;
}
if(o1 instanceof Student && o2 instanceof Student){//如果类型匹配才转换
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
if(stu1.age>stu2.age){//先判断年龄后判断姓名
return 1;
}else if(stu1.age<stu2.age){
return -1;
}else{
return stu1.name.compareTo(stu2.name);
}
}
return 0;
}
}
测试代码
MyComparator mc = new MyComparator();
TreeSet ts = new TreeSet(mc);
Student stu1 = new Student("XXX",19);
Student stu2 = new Student("OOO",18);
Student stu3 = new Student("YYY",18);
Student stu4 = new Student("ZZZ",18);
Student stu5 = new Student("QQQ",18);
ts.add(stu1);
ts.add(stu2);
ts.add(stu3);
ts.add(stu4);
ts.add(stu5);
System.out.println(ts.size());
System.out.println(ts);
5
[OOO-18, QQQ-18, YYY-18, ZZZ-18, XXX-19]
8.5.TreeSet判断重复的标准小结
如果采用的是自然排序调用对象的compareTo方法,如果返回0 表示相等;
大于且返回正数,升序排列 => 小于且返回负数,升序排列 对应
大于且返回负数,降序排列 => 小于且返回正数,降序排列 对应
如果使用的定制排序(比较器),调用比较器的方法compare 返回0 表示相等;
大于且返回正数,升序排列 => 小于且返回负数,升序排列 对应
大于且返回负数,降序排列 => 小于且返回正数,降序排列 对应
9.Collection体系
10.课程总结
10.1.重点
10.2.难点
11.常见面试题
1.请说说ArrayList与LinkedList的区别
1.下面代码打印输出结果是:
LinkedList list = new LinkedList();
list.push("A");
list.push("B");
list.push("C");
list.push("D");
list.push("E");
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.pop());
}
12.课后练习
1.把今天上课所有的代码敲熟练,明天抽查方法的基本使用;
2.把Iterator和Iterable的区别和作用每人都手抄至少两遍
13.每日一练
1.请说出四大访问权限修饰符的作用域
2.Overload与Override的区别