Java-Collection集合(三)

一、Set集合

Set接口:java.util.Set
Set接口继承了Collection接口,因此所有的方法也都可以使用,它并没有对Collection接口的功能进行扩充,只是存储规则更加严格。
Set接口特点:
1.无序,存储元素与取出元素的顺序可能不一致
2.不容许存储重复元素
3.没有带索引的方法,不能使用普通for循环进行遍历
注意:Set接口也不能直接创建对象使用,需要创建实现类对象进行使用,Set接口最常用的两个实现类为HashSet和LinkedHashSet。

public class SetDemo01 {
	public static void main(String[] args) {
		//使用多态,创建一个Set接口对象
		Set<Integer>  set = new HashSet<>();
		//存储一些元素,添加一些重复元素
		set.add(3);
		set.add(2);
		set.add(2);
		set.add(1);
		//看出Set集合存储元素与输入顺序不一致,而且不容许存储重复元素
		System.out.println(set);//输出结果:[1, 2, 3] 
		
		
		//遍历方法 
		//1.创建迭代器
		Iterator<Integer> it = set.iterator();
		while(it.hasNext()){
			Integer i = it.next();
			System.out.print(i+" ");//输入结果:1 2 3 
		}
		//2.增强for
		for(Integer i:set){
			System.out.println(i+" ");//输入结果:1 2 3 
			
		}
		//删除元素方法
	    boolean b = set.remove(2);
	    System.out.println(b);//输出结果:true
	    
	    //返回集合元素个数
	    System.out.println(set+" ");//输出结果:[1, 3] 
	    int size = set.size();
	    System.out.println(size);//输出结果:2
	    
	    //判断集合是否为空
	    boolean c = set.isEmpty();
	    System.out.println(c);//输出结果:false
	    
	    //判断集合是否包含指定元素
	    boolean d = set.contains(1);
	    System.out.println(d);//输出结果:true
	    
	    //清空集合中的元素
	    set.clear();
	    System.out.println(set);//输出结果:[]
	}

}

二、HashSet集合

HashSet集合:java.util.HashSet
HashSet集合是Set接口的实现类,它包含了所有Set接口的特性,根据对象的哈希值来确定元素的具体位置,底层是一个Hash表结构,因此查找速度很快。

HashSet存储结构:
哈希值:一个十进制整数,系统随机给出,本质上是对象的逻辑地址值,是模拟出来的,不是实际存储的物理地址。 在Object类中一个方法,可以用来获取对象的哈希值:
public int hashCode(); (可以进行重写)

哈希表结构:
在JDK1.8之前底层采用数组+链表的结构,JDK1.8及以后底层采用数组+链表+红黑树结构,当链表结构超过8,自动转成红黑树 可以减少查找时间。数组的作用是把元素进行分组,将相同哈希值的元素存在一起,链表/红黑树将相同哈希值的元素连接到一起。

public class SetDemo02 {
	public static void main(String[] args) {
		//Obeject类是所有类的根类,其他类可以使用HashCode方法
		//使用系统定义好的类,已经重写了toString方法
		Integer i1 = new Integer(1);
		Integer i2 = new Integer(2);
		int h1 = i1.hashCode();
		int h2 = i2.hashCode();
		System.out.println(h1);//1
		System.out.println(h2);//2
        //注意:String类中有两个字符串"重地"、"通话"的哈希值正好相同
		String s1 = "abcd";
		String s2 = "abcd";
		System.out.println(s1.hashCode());//输出结果:2987074
		System.out.println(s2.hashCode());//输出结果:2987074
		
		String s3 = "重地";//输出结果:1179395
		String s4 = "通话";//输出结果:1179395
		System.out.println(s3.hashCode());
		System.out.println(s4.hashCode());
		
		
		//使用自定义的类
		Demo d1 = new Demo();
		Demo d2 = new Demo();
		int h3 = d1.hashCode();
		int h4 = d2.hashCode();
		System.out.println(h3);//重写hashCode()之前输出结果:366712642,重写hashCode()之后输出结果:666
		System.out.println(d1);//重写hashCode()之前输出结果:set集合.Demo@15db9742,重写hashCode()之后输出结果:set集合.Demo@29a
		System.out.println(h4);//重写hashCode()之前输出结果:1829164700,重写hashCode()之后输出结果:666
		System.out.println(d2);//重写hashCode()之前输出结果:set集合.Demo@6d06d69c,重写hashCode()之后输出结果:set集合.Demo@29a
		//重写HashCode()以后,改变的是逻辑地址,不改变物理地址,逻辑地址可能相同,但物理地址不同
		System.out.println(d1.equals(d2));//输出结果:false  实际物理地址不同
		
		
		
		
	}

}

class Demo{
	//重写hashCode()
	@Override
	public int hashCode() {
		return 666;
	}
	
	
}

三、Set集合存储原理

Set集合不容许存储重复元素原理:
调用add方法时,add方法调用HashCode方法和equals方法,判断元素是否重复。(hashCode比较逻辑地址,equals比较的是物理地址)在进行新元素传入时,首先调用HashCode方法进行新元素的哈希值计算,在集合中查找是否有 相同哈希值的元素,如果没有就会把元素存进集合中,如果集合中有相同哈希值元素(也称哈希冲突) ,会调用equals方法,对两个相同哈希值的元素进行判断,如果equals方法返回true,则将 新元素存进集合中,如果equals方法返回false,则不将新元素存进集合

HashSet集合存储自定义类型元素
因为之前系统已经定义的类型,已经重写了hashCode和equals方法,想要元素保证唯一, 自定义类型元素,必须重写hashCode和equals方法,建立自己的比较方式.

public class SetDemo03 {
	public static void main(String[] args) {
		HashSet<String>  hset = new HashSet<>();
		hset.add("abcd");
		hset.add("abcd");
		hset.add("重地");
		hset.add("通话");
		System.out.println(hset);//输出结果:[重地, 通话, abcd]  不存储重复元素
		
		
		HashSet<Demo01>  hset1 = new HashSet<>();
		Demo01 demo01 = new Demo01(20,"张三");
		Demo01 demo02 = new Demo01(20,"张三");
		Demo01 demo03 = new Demo01(21,"张三");
		hset1.add(demo01);
		hset1.add(demo02);
		hset1.add(demo03);
		System.out.println(hset1);
		/*
		重写方法前输出结果:[Demo01 [age=21, name=张三], Demo01 [age=20, name=张三], Demo01 [age=20, name=张三]]
		发现 两个包含相同的元素的对象也存储进去了,原因是自定义的类没有重写HashCode和equals方法,两个包含相同的元素
		的对象的哈希值	不同,所以可以被存储,需要重写HashCode和equals方法
	    
	    
	       重写方法后输出结果:[Demo01 [age=20, name=张三], Demo01 [age=21, name=张三]],存储的都是不重复元素的对象
		 */
		System.out.println(demo01.hashCode());//重写方法前输出结果:366712642,重写方法后输出结果:776470
		System.out.println(demo02.hashCode());//重写方法前输出结果:1829164700,重写方法后输出结果:776470
		System.out.println(demo01.equals(demo02));//重写方法前输出结果:false,重写方法后输出结果:true
	}

}

class Demo01{
	public int age;
	public String name;
	
	public Demo01(int age, String name) {
		this.age = age;
		this.name = name;
	}

	@Override
	public String toString() {//重写toString方法
		return "Demo01 [age=" + age + ", name=" + name + "]";
	}
    
	
	//重写hashCode和equals方法,可以使用系统自动生成的即可
	@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;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Demo01 other = (Demo01) 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;
	}
	
	
	
	
}

四、LinkedHashSet集合

LinkedHashSet集合:
java.util.LinkedHashSet implements java.util.HashSet
底层是哈希表+链表,新链表的作用记录元素的存储顺序,使得元素变的有序。

public class SetDemo04 {
	public static void main(String[] args) {
		HashSet<String>  hset = new HashSet<>();
		hset.add("abcd");
		hset.add("abcd");
		hset.add("重地");
		hset.add("通话");
		System.out.println(hset);//输出结果:[重地, 通话, abcd]  无序,不存储重复元素
		
		
		LinkedHashSet<String>  hset1 = new LinkedHashSet<>();
		hset1.add("abcd");
		hset1.add("abcd");
		hset1.add("重地");
		hset1.add("通话");
		System.out.println(hset1);//输出结果:[abcd, 重地, 通话]  有序,不存储重复元素
		
	}
 
}

五、可变参数

可变参数:当方法参数列表数据类型已经确定,但是不确定参数的个数,可以使用可变参数。 底层是一个数组,根据传递参数的个数不同,会创建不同长度得数组,参数的个数可以从0个到多个。
使用格式(定义方法时使用):
修饰符 返回值类 参数列表(数据类型…变量名){
方法体
}

注意事项:
1.一个方法的参数列表,只能有一个可变参数。
2.如果一个方法参数有多个,可变参数必须放在参数列表末尾。

参数特殊格式:
修饰符 返回值类 参数列表(Obiect…变量名){//可以传递任意数据类型数据
方法体
}

public class SetDemo06 {
	public static void main(String[] args) {
		/*
		调用addMethod方法时,根据参数的个数进行不同长度的数组的创建
		例如:
		addMethod():创建一个长度为0的数组,new int[0]
		addMethod(1):创建一个长度为1的数组,new int[]{1}
		addMethod(1,2):创建一个长度为1的数组,new int[]{1,2}
		......以此类推
		 */
		int i =addMethod();
		int i1 = addMethod(1);
		int i2 = addMethod(1,2);
		int i3 = addMethod(1,1,1,1,1,1,1);
		System.out.println(i);//输出结果:0
		System.out.println(i1);//输出结果:1
		System.out.println(i2);//输出结果:2
		System.out.println(i3);//输出结果:7
		
	   method(1,1.11, "aa","bb","cc");
	   method4(1,1.11, "aa","bb","cc");//可以传递任意数据类型数据
	}

	
	//定义一个可以进行任意个数累加和的方法
	public static int addMethod(int...array){
//		System.out.println(array);//输出结果:[I@15db9742 打印的是数组地址值,[表示数组,I表示int类型
//		System.out.println(array.length);//输出结果:0  
		
		//进行求和
		int sum = 0;
		for(int i:array){
			sum=sum+1;
		}
		return sum;
		
	}
	
	
	//1.一个方法的参数列表,只能有一个可变参数
	public static void method(int i,double d,String...s){} //正确写法
	//public static void method1(int i,double...d,String...s){}//错误写法
	
	
	// 2.如果一个方法参数有多个,可变参数必须放在参数列表末尾
	public static void method2(int i,double d,String...s){} //正确写法
	//public static void method3(String...s,int i,double d){}//错误写法
	
	
	//可变参数特殊格式:
	public static void method4(Object...obj){}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值