A018_List_Set集合


集合框架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的区别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值