Java 知识点整理-11.Java集合框架 Set+HashSet+LinkedHashSet+TreeSet+List和Set迭代方式的区别

目录

Set

HashSet

LinkedHashSet

TreeSet

List和Set迭代方式的区别:


Set

1、Set集合概述及特点:

public interface Set<E> extends Collection<E> 不包含重复元素的集合。 更正式地,集合不包含一对元素e1和e2 ,使得e1.equals(e2) ,并且最多一个空元素。

Set接口继承自Collection接口的所有构造函数(方法)。Set与Collection方法一模一样。

2、Set集合子类如何保证元素唯一?

HashSet

1、Set集合子类HashSet<E>概述:

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable ,此类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。 它不保证set的迭代顺序; 特别是它不保证该顺序在一段时间内保持不变。此类允许使用null元素。翻译:存取顺序不一致,可以存储null。

 2、案例演示:HashSet存储字符串并遍历。

import java.util.HashSet;
/**
 * set集合中元素无索引,不可以重复,无序(存取顺序不一致)
 */
public class Demo1_HashSet {
	public static void main(String[] args) {
		HashSet<String> hs = new HashSet<>();		//创建HashSet对象。
		boolean b1 = hs.add("a");
		boolean b2 = hs.add("a");					//当向Set集合中存储重复元素的时候返回为false。
		
		System.out.println(hs);						//HashSet的继承体系中有重写toString方法
		System.out.println(b1);						//true
		System.out.println(b2);						//false
		//HashSet的爷爷类AbstractCollection重写了toString()
		/*public String toString() {			
	        Iterator<E> it = iterator();						//获取迭代器
	        if (! it.hasNext())									//如果迭代器中没有元素
	            return "[]";									//返回[]

	        StringBuilder sb = new StringBuilder();				//如果有元素,创建StringBuilder
	        sb.append('[');										//将元素不断进行添加
	        for (;;) {
	            E e = it.next();
	            sb.append(e == this ? "(this Collection)" : e);
	            if (! it.hasNext())
	                return sb.append(']').toString();			//最后将元素转换成toString给返回
	            sb.append(',').append(' ');
	        }
	    }*/
		System.out.println("----------------------------------");
		hs.add("d");
		hs.add("c");
		hs.add("b");
		System.out.println(hs);
		
		for (String string : hs) {					//只要能使用迭代器迭代的,就可以使用增强for循环遍历。
			System.out.println(string);
		}
	}
}

3、案例演示:HashSet存储自定义对象,并保证元素唯一性。

import java.util.HashSet;

import com.bean.Person;

public class Demo2_HashSet {
	public static void main(String[] args) {
		HashSet<Person> hs = new HashSet<>();
		hs.add(new Person("张三", 23));
		hs.add(new Person("张三", 23));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		
		//即使重写了自定义类中的equals方法,依然所有元素都在。因为并没有执行。
		//重写了hashCode方法后,保证元素唯一了,我们并没有调equals方法,equals方法也跟着执行了。
		//集合的每个对象元素进入集合的内存空间时,对象元素会自动调对应自定义类的hashCode()给自己算一个具体的hashCode值(地址值),每个对象的hashCode值都不一样所以不会调equals()进行比较。因为地址值都不一样,所以重复的对象都保留了。
		//重写hashCode()给定一个固定的值(任意),不让自己算了。地址值相同的对象元素,调用equals()进行比较,如果元素属性值相同,则返回值是true,就不存了。如果元素属性不同,HashSet有桶结构,新元素会像桶一样挂在被比较的元素上。再进来的元素会先与挂着的元素比较。
		System.out.println(hs.size());
		System.out.println(hs);
	}
}

重写hashCode()和equals()方法
public boolean equals(Object obj) {
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age == p.age;
}
public int hashCode() {
		return 10;
}

代码优化:

为了减少比较,优化hashCode()代码写法。最终版就是自动生成即可。

Alt + Shift + S + H 自动生成hashCode()和equals()。

//这么复杂是为了少调用equals(),提高程序的运行效率。
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	public boolean equals(Object obj) {
		if (this == obj)					//传入对象等于调用对象,说明两个引用指向同一个对象。
			return true;					//直接返回true
		if (obj == null)					//传入的对象为null
			return false;					//返回false
		if (getClass() != obj.getClass())	//调用的字节码对象和传入的字节码对象不是同一个。内存中同名的字节码文件只能有一个。
			return false;					//直接返回false。 到这里,判断了传入对象一定是自定义对象,跟被调用对象不是同一个对象,肯定是有值的,和被调用对象属于同一个字节码文件生成的对象。
		Person other = (Person) obj;		//向下转型 因为上一步进行过判断,所以不会出现类型转换异常。
		if (age != other.age)				//调用对象的年龄不等于传入对象的年龄
			return false;					//返回false
		if (name == null) {					//调用对象的name属性为null,name是自定义对象的属性,是可以赋值为null的(不给赋值),不存在空指针异常。
			if (other.name != null)			//传入对象的name属性不为null
				return false;				//返回false
		} else if (!name.equals(other.name))//调用对象的姓名不等于传入对象的姓名
			return false;					//返回false
		return true;						//返回true
	}

prime等于31是因为:

31是一个质数,质数是仅能被1和自己整除的数,这样他的公约数就少了,就可以降低重复的可能性。

31这个数既不大也不小。大了,进行运算之后可能超过int的取值范围,小了,最后相乘的结果可能还是重复的。能不重复尽量不重复。

31这个数好算,2的5次方减1,2向左移动5位再减去一。

4、HashSet如何保证元素唯一性的原理:

1.HashSet原理:

我们使用Set集合都需要去掉重复元素,如果在存储的时候逐个元素调用equals()进行比较,效率较低。哈希算法提高了去重复的效率,降低了使用equals()的次数。

当HashSet调用add()方法存储对象的时候,先调用对象的hashCode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象。

如果没有哈希值相同的对象就直接存入集合。

如果有哈希值相同的对象,就和哈希值相同的对象逐个元素调用equals()进行属性值比较,比较结果为false就存入,为true则不存入。

2.将自定义类的对象存入HashSet去重复:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值