day06 set集合

本文详细介绍了Java集合框架中的Set接口及其特点,包括无序性、唯一性和哈希值的概念。特别强调了HashSet和TreeSet的区别,HashSet依赖hashCode()和equals()保证唯一性,而TreeSet通过自然排序或比较器实现排序。同时,文章讨论了泛型的基本概念、泛型类、泛型方法和泛型接口的应用,以及类型通配符的使用。此外,还提到了可变参数在方法定义中的作用和本质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Set集合
一、Set集合的概述
public interface Set
extends Collection

	1.Set集合是一个接口,所以就不能直接创建对象,只能使用多态的方式创建对象
	2.set集合是一个单列集合

二、特点
	(1)无序:元素的存取顺序
	(2)没有索引
		//所以set集合不能使用普通for循环遍历
	(3)唯一:集合中没有重复的元素
	
	//既然set集合中的元素是唯一的,那么我向set集合中添加已有元素会报错吗?
		不会报错


三、哈希值
	1.什么是哈希值
		就是根据"地址值"或者"内部元素属性值"计算出来的一个int数值
		
		(1)什么时候根据"地址值"计算?
			Object类中的hashCode方法就是根据"地址值"来计算的。
		
		(2)什么时候又根据"内部元素的属性值"计算?
			就是在任意一个已经重写了hashCode方法后的类
				//比如:String、Integer、Character。。。。。

		//问题:如果元素的属性值不同,那么hashCode的返回值会不会相同?
			有可能,只不过几率较低。   //"通话"   "重地"
				因为hashCode方法的返回值是一个int类型的值,而int类型是有范围的(正负21亿),也就是说哈希值最多只有42亿左右个,但是我可以写
				50亿个字符串,那么这个时候肯定至少会有将近8亿个哈希值是重复的

四、hashset集合的概述和特点
	1.hashset集合的特点和set集合一毛一样
	2.底层结构
		哈希表(数组+链表)

	3.set集合是怎么保证唯一性的      //非常常见的面试题
		依赖了hashCode()和equals()方法
			1.首先计算要添加的元素的hashCode值
				(1)哈希表中不存在该哈希值
					直接将元素添加到哈希表中
				(2)哈希表中已经存在该哈希值
					使用equals方法和该哈希值下面"桶"中的数据逐一比较
						*发现里面有要添加的元素,覆盖
						*发现里面没有要添加的元素,在"桶"中继续添加该元素

五、HashSet集合存储学生对象并遍历
	1.在创建标准的Student的Javabean类的时候,不要忘记添加toString方法
	2.一定要在Student类中重写hashCode和equals方法
		//因为如果不重写这两个方法,那么调用hashcode方法时,会去使用Student的父类Object的hashCoe,这时是用地址值去计算的。不能根据属性值相同就认为是同一个对象
		
	3.怎么重写hashCode和equals方法
		idea自动生成
			快捷键 ALT + insert  -----------  选择hashCode() and equals  自动生成即可
			
六、LinkedHashSet的概述和特点
	1.底层结构 
		链表 + 哈希表
		
	2.特点
		唯一:由哈希表保证
		存取有序:由链表保证的
		
	3.应用
		//去重
			代码实现:
				public class Demo4 {
					public static void main(String[] args) {
						LinkedHashSet<String> h1 = new LinkedHashSet<>();
						h1.add("hello");
						h1.add("world");
						h1.add("java");
						h1.add("hello");

						System.out.println(h1);
					}
				}

TreeSet
一、TreeSet集合的概述和特点
1.概念
能够对元素按照 “某种规则” 进行排序的集合

	2.特点
		(1)唯一  (元素不能重复)
		(2)有序  (按照某种规则)  //不是存取顺序

	3.TreeSet集合的两种排序方式
			//前提:就是我们必须给出一个排序的规则
		(1)自然排序
			//要求:在创建TreeSet集合对象的时候,构造方法的参数为空
				TreeSet<Student> ts = new TreeSet<>();
				
			//怎么给出排序的规则(接口)
				TreeSet集合创建对象时,<>里面的泛型类型必须去实现 Comparable,重写里面的compareTo方法
					代码实现: 
						public class Demo5 {
							public static void main(String[] args) {
								TreeSet<Student> ts = new TreeSet<>();
								Student s1 = new Student("王小二",12);
								Student s2 = new Student("王小三",13);

								ts.add(s1);
								ts.add(s2);
								System.out.println(ts);
							}
						}
						
				----------------------------------------------------------------------------
				public class Student implements Comparable<Student> {
					private String name;
					private int age;
					@Override
					public int compareTo(Student o) {
						return this.age - o.age;    //这里的this指的是添加进来的Student对象, o代表的二叉树的节点
					}
				}		
		(2)比较器排序
			//要求:在创建TreeSet集合对象的时候,构造方法的参数必须传入参数
				TreeSet​(Comparator<? super E> comparator) 构造一个新的,空的树集,根据指定的比较器进行排序。  //常用匿名内部类
					//注意:这个时候排序规则在Comparator接口的compare方法中,所以<>泛型中的类就不需要再去实现 Comparable
					代码实现: 
						public class Demo5 {
						public static void main(String[] args) {
							TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
								@Override
								public int compare(Student o1, Student o2) {
									int num = o1.getAge() - o2.getAge();
						   /* if (num==0){
								num = this.name.compareTo(o.name);
							}*/

									int num2 = num == 0?o1.getName().compareTo(o2.getName()):num;
									return num2;
								}
							});
							Student s1 = new Student("王小二",12);
							Student s2 = new Student("王小三",13);
							Student s3 = new Student("王小四",14);
							Student s4 = new Student("王小五",13);
							Student s5 = new Student("王小三",15);

							ts.add(s1);
							ts.add(s2);
							ts.add(s3);
							ts.add(s4);
							ts.add(s5);

							System.out.println(ts);
						}
					}
		
		(3)自然排序和比较器排序的区别
			//区别主要在于传入排序规则的方式不同
				自然排序 :排序规则是在<>泛型中实现  Comparable ,然后在里面compareTo中定义的
					TreeSet<Student> ts = new TreeSet<>();  //  *构造方法必须为空   * <> 中的Student必须实现Comparable
		
				比较器排序:在创建TreeSet集合的时候,将规则以Comparator接口中compare中定义的
					TreeSet<Student> ts = new TreeSet<>(new Comparator(){xxx});   // *构造方法必须传入Comparator对象,  * <>中的Student不需要实现接口
		
		
		(4)成绩排序案例
			代码实现:(自然排序方式)
				public class Student implements Comparable<Student> {
						private String name;
						private int chinese;
						private int math;

						public Student() {
						}

						public Student(String name, int chinese, int math) {
							this.name = name;
							this.chinese = chinese;
							this.math = math;
						}

						public String getName() {
							return name;
						}

						public void setName(String name) {
							this.name = name;
						}

						public int getChinese() {
							return chinese;
						}

						public void setChinese(int chinese) {
							this.chinese = chinese;
						}

						public int getMath() {
							return math;
						}

						public void setMath(int math) {
							this.math = math;
						}

						@Override
						public String toString() {
							return "Student{" +
									"name='" + name + '\'' +
									", chinese=" + chinese +
									", math=" + math +
									'}';
						}

						public int getSum() {
							return chinese + math;
						}


						@Override
						public int compareTo(Student s) {
							//先比较总分
							int num1 = s.getSum() - this.getSum();

							//总分相同,在按照语文成绩比
							if (num1 == 0) {
								num1 = s.getChinese() - this.getChinese();
							}

							//语文成绩相同,按照姓名比
							if (num1 == 0) {
								num1 = s.getName().compareTo(this.getName());
							}
							return num1;
						}
					}
		---------------------------------------------------------------------------------------
			public class Demo1 {
				public static void main(String[] args) {
					TreeSet<Student> ts = new TreeSet();
					Student s1 = new Student("王小二",99,89);
					Student s2 = new Student("王小三",96,83);
					Student s3 = new Student("王小四",90,100);
					Student s4 = new Student("王小五",89,99);

					ts.add(s1);
					ts.add(s2);
					ts.add(s3);
					ts.add(s4);

					for (Student s : ts){
						System.out.println(s.getName()+"==="+s.getSum());
					}
				}
			}
		
		//set集合的唯一性分类
	*hashset
		(1)底层结构是哈希表(数组 + 链表)
		(2)保证唯一性的方式
			//依赖了hashCode()和equals()方法
	
	*treeset
		//TreeSet集合和hashCode()和equals()没有直接的关系
		(1)底层结构是二叉树
		(2)保证唯一性的方式
			就是当比较的规则返回值为0时,我们会认为是同一个元素,不添加
			
	4.不重复的随机数案例
		因为要的是不重复的随机数,所以考虑使用set集合
			*hashset :不推荐  存取无序
			*TreeSet :按照从小到大
			*LinkedHashSet:按照存取的顺序

泛型 //了解
一、泛型的概述
1.什么是泛型?
泛:泛指
型:某一种"引用数据"类型
泛型:就是泛指某一种引用数据类型,但是又不确定是哪一种数据类型,一旦明确,就只能是一种数据类型(不能是多种)
public class ArrayList

			明确的时机:
				(1)创建对象的时候
					ArrayList<String>  arr  =  new ArrayList<>();
				(2)调用方法的时候
				(3)一个类实现接口的时候

	2.泛型的作用
		限定了放入集合中的元素的类型,将类型限定为同一种类型
		
	3.好处
		(1)把运行时期的问题提前到了编译期间
		(2)避免了强制类型转换
		
	4.注意事项
		泛型中的类型只能是引用数据类型
		
		
二、泛型类    //掌握
	1.什么是泛型类?
		就是在普通类上加一个<E>  ,就变成了泛型类
		
	2.格式
		class 类名<E>{
			
		}
			举例:
			class Student<E>{  }
				<E>里面的字母有什么要求?
					这里的字母可以随意给,A、B、C     常用的E(Element)
		
	3.明确时机
		//在创建对象的时候 
			泛型类:public class ArrayList<E>    
			明确时机:ArrayList<String>  arr  =  new ArrayList<>();
		
三、泛型方法
	1.什么是泛型方法
		就是在普通方法上加一个<E>  ,就变成了泛型方法
		
	2.格式
		修饰符 <T>  返回值  方法名 (T  t){
			
		}
			举例:
				  public <A> void method(A a){
						   System.out.println(a);
					   }
								
		
	3.明确的时机 
		//在调用方法的时候
			代码实现:
				 public static void main(String[] args) {
					StudentFX sfx = new StudentFX();
					sfx.method("王小二");  //明确为String
					sfx.method(15);  //明确为Integer 
					sfx.method(12.58); //明确为Double
					sfx.method(false); //明确为Boolean
				}
				
四、泛型接口
	1.什么是泛型接口
		就是在普通接口上加一个<E>  ,就变成了泛型接口
		
	2.格式
		public  interface  接口名<T>{
			void show(T t);
		}
		
	3.明确时机
		(1)就是在类实现接口的时候
			public class MathImpl implements Math<String> {
					@Override
					public void study(String s) {
						System.out.println(s);
					}
				}

				class Test3{
					public static void main(String[] args) {
						MathImpl  mi1 = new MathImpl();
						mi1.study("王小二");
					}
				}
				
		(2)将泛型传递下去
			public class MathInpl2<T> implements Math<T> {
					@Override
					public void study(T t) {
						System.out.println(t);
					}
				}


				class Test4{
					public static void main(String[] args) {
						MathInpl2<Integer> mi2 = new MathInpl2<>();
						mi2.study(13);
					}
				}

类型通配符 (泛型约束) //了解
1.类型通配符:<?> List<?>:表示元素类型未知的List,它的元素可以匹配"任何的类型"
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
举例:
ArrayList<?> arr = new ArrayList(); ArrayList<?> arr1 = new ArrayList();
ArrayList<?> arr2 = new ArrayList();

2.类型通配符上限:<? extends 类型>
	List<? extends Number>:它表示的类型是Number或者其子类型   //小于等于的关系
		举例:
			ArrayList<? extends Number> arr = new ArrayList<Object>();   //报错:  因为object是number的父类。而这里限定的是number及其子类
			ArrayList<? extends Number> arr1 = new ArrayList<Number>();
			ArrayList<? extends Number> arr2 = new ArrayList<Integer>();
3.类型通配符下限:<? super 类型>
	List<? super Number>:它表示的类型是Number或者其父类型   //大于等于的关系
		举例:
			ArrayList<? super Number> arr = new ArrayList<Object>();
			ArrayList<? super Number> arr1 = new ArrayList<Number>();
			ArrayList<? super Number> arr2 = new ArrayList<Integer>();  //报错:因为Integer是number的子类,而这里限定的是number及其父类
			

	//泛型约束到底是约束谁的?
		ArrayList<? extends Number> arr1 = new ArrayList<Number>();
			约束的其实就是=后面new的那个对象的泛型
		
		
	java.lang.Object 
		java.lang.Number 
			java.lang.Integer 

可变参数 //熟悉 因为在后期我们看源码的时候会经常遇到
一、概述
1.什么是可变参?
就是我们在定义方法的时候,不知道要定义多少个参数,这个时候我们就可以考虑使用可变参数

	2.格式
		修饰符  返回值  方法名 (int... a){
			xxxx
		}
		
	3.本质
		//其实可变参数就是一个数组
			数组: int[] ints = new int[10];
			可变参: int... ints     //  这里的int相当于数组中的元素类型,ints相当于数组的对象名称
					
	4.注意事项 
		如果一个方法有多个参数,包含可变参数,可变参数要放在最后
				//如果方法的参数中有多个可变参,那么需要把前面的可变参都改成数组,最后一个保留可变参就行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值