Java——集合2-3(Set)

Set

Set集合概述和特点

Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环遍历

Set集合练习

  • 存储字符串并遍历
import java.util.HashSet;
import java.util.Set;

/*
Set集合特点

 不包含重复元素的集合
 没有带索引的方法,所以不能使用普通for循环遍历

HashSet:对集合的迭代顺序不作任何保证

*/
public class SetDemo {
	public static void main(String[] args) {
		//创建集合对象
		//HashSet:对集合的迭代顺序不作任何保证
		Set<String> set = new HashSet<String>();

		//添加元素
		set.add("hello");
		set.add("world");
		set.add("java");
		//不包含重复元素
		set.add("hello");

		//遍历
		for (String s : set) {
			System.out.println(s);
		}
		/*运行结果:
		world
		java
		hello
		*/

	}
}

运行结果:
在这里插入图片描述
哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode():返回对象的哈希码值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
/*
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值:
	public int hashCode():返回对象的哈希码值
*/

public class HashDemo {
	public static void main(String[] args) {
		//创建学生对象
		Student s1 = new Student("小白", 12);
		//同一个对象多次调用hashCode()方法返回的哈希值是相同的
		System.out.println(s1.hashCode());//1854778591
		System.out.println(s1.hashCode());//1854778591
		System.out.println("--------");

		//创建学生对象
		Student s2 = new Student("小白", 22);
		//默认情况下,不同对象的哈希值是不相同的
		//通过方法重写,可以实现不同对象的哈希值相同
		System.out.println(s2.hashCode());
		System.out.println("--------");

		System.out.println("hello".hashCode());//99162322
		System.out.println("world".hashCode());//113318802
		System.out.println("java".hashCode());//3254818

		System.out.println("world".hashCode());//113318802
		System.out.println("--------");

		System.out.println("重地".hashCode());//1179395
		System.out.println("通话".hashCode());//1179395
		System.out.println("程序".hashCode());//992740
		System.out.println("代码".hashCode());//656766
	}
}

运行结果:
在这里插入图片描述
HashSet集合概述和特点

HashSet集合特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储组和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合练习

  • 存储字符串并遍历
public class HashSetDemo {
	public static void main(String[] args) {
		//创建集合对象
		HashSet<String> hs = new HashSet<String>();

		//添加元素
		hs.add("hello");
		hs.add("world");
		hs.add("java");
		hs.add("world");
		//遍历
		for (String s : hs) {
			System.out.println(s);
		}

	}
}

运行结果:
在这里插入图片描述
HashSet集合保证元素唯一性源码分析

//创建集合对象
HashSet hs = new HashSet();

//添加元素
hs.add(“hello”);
hs.add(“world”);
hs.add(“java”);
hs.add(“world”);


public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

//hash值和元素的hashCode()方法相关

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;

//如果哈希表未初始化,就对其进行初始化

if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;

// 根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
存入的元素和以前的元素 比较哈希值
如果哈希值不同,会继续向下执行,把元素添加到集合
如果哈希值相同,会调用对象的equals()方法比较
如果返回false,会继续向下执行,把元素添加到集合
如果返回true,说明元素重复,不存储

*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

HashSet集合存储元素:

  • 要保证元素唯一性,需要重写hashCode()equals()

常见数据结构之哈希表

哈希表

  • JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
  • JDK8之后,在长度比较长的时候,底层实现了优化

案例:HashSet集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象

import java.util.Objects;

//定义学生类
public class Student {
	private String name;
	private int age;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	//快捷键重写hashCode()和equals()
	@Override
	public int hashCode() {
		return Objects.hash(age, name);
	}

	@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;
		return age == other.age && Objects.equals(name, other.name);
	}

}
import java.util.HashSet;

/*
案例:HashSet集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象

*/
public class HashSetDemo2 {
	public static void main(String[] args) {
		//创建HashSet集合对象
		HashSet<Student> hs = new HashSet<Student>();

		// 创建学生对象
		Student s1 = new Student("小白", 12);
		Student s2 = new Student("小黑", 13);
		Student s3 = new Student("小红", 11);

		Student s4 = new Student("小红", 11);

		// 把学生添加到集合
		hs.add(s1);
		hs.add(s2);
		hs.add(s3);
		hs.add(s4);

		//遍历集合
		for (Student s : hs) {
			System.out.println(s.getName() + "," + s.getAge());
		}

	}
}

运行结果:
在这里插入图片描述

LinkedHashSet集合概述和特点

LinkedHashSet集合特点:

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 有链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 有哈希表保证元素唯一性,也就是说没有重复的元素

LinkedHashSet集合练习

  • 存储字符串并遍历
import java.util.LinkedHashSet;

/*
LinkedHashSet集合特点:

- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 有链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 有哈希表保证元素唯一性,也就是说没有重复的元素

LinkedHashSet集合练习

- 存储字符串并遍历
*/
public class LinkedHashSetDemo {
	public static void main(String[] args) {
		//创建集合对象
		LinkedHashSet<String> lhs = new LinkedHashSet<String>();

		//添加元素
		lhs.add("hello");
		lhs.add("world");
		lhs.add("java");
		lhs.add("world");

		//遍历
		for (String s : lhs) {
			System.out.println(s);
		}

	}
}

运行结果:
在这里插入图片描述
TreeSet集合概述和特点

TreeSet集合特点:

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
    TreeSet():根据其元素的自然排序进行排序
    TreeSet(Comparator comparator):根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复元素的集合

TreeSet集合练习

  • 存储整数并遍历
import java.util.TreeSet;

/*
TreeSet集合特点:

- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素的集合

TreeSet集合练习

- 存储整数并遍历
*/
public class TreeSetDemo {
	public static void main(String[] args) {
		//创建集合对象
		TreeSet<Integer> ts = new TreeSet<Integer>();//由于<>里要用引用类型,所以要用int的包装类Integer

		//添加元素
		ts.add(55);
		ts.add(22);
		ts.add(44);
		ts.add(33);
		ts.add(11);

		ts.add(11);//不包含重复元素

		//遍历集合
		for (int i : ts) {
			System.out.println(i);
		}
		/*运行结果:按照自然顺序从小到大排序
		11
		22
		33
		44
		55
		*/

	}
}

运行结果:
在这里插入图片描述
自然排序Comparable的使用

  • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
//定义学生类
public class Student implements Comparable<Student> {
	private String name;
	private int age;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public int compareTo(Student s) {
		// TODO Auto-generated method stub
		//		return 0;//相同,不存储
		//		return 1;//存储的正序
		//		return -1;//存储的逆序

		//按照年龄从小到大排序
		int num = this.age - s.age;//这里this表示s2,s表示s1
		//		int num=s.age-this.age;//降序排列

		//年龄相同时,按照姓名的字母顺序排序
		int num2 = num == 0 ? this.name.compareTo(s.name) : num;//因为自然排序本身就可以用string类型,所以直接用s.name

		return num2;//可以保证元素的唯一性
	}

}

import java.util.TreeSet;

/*
自然排序Comparable的使用

- 存储学生对象并遍历,创建TreeSet集合使用**无参构造方法**
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/

public class TreeSetDemo2 {
	public static void main(String[] args) {
		//创建集合对象
		TreeSet<Student> ts = new TreeSet<Student>();

		//创建学生对象
		Student s1 = new Student("xiaobai", 12);
		Student s2 = new Student("dabai", 13);
		Student s3 = new Student("xiaohei", 14);
		Student s4 = new Student("dahei", 10);

		Student s5 = new Student("xiaohong", 10);
		Student s6 = new Student("xiaohong", 10);//元素唯一

		//学生添加到集合
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);

		//遍历集合
		for (Student s : ts) {
			System.out.println(s.getName() + "," + s.getAge());
		}
		/*运行结果:
			ClassCastException  
			因为:学生类没有实现Comparable接口
		*/

		/*运行结果:
			xiaobai,12   
			因为:此时compareTo()里返回的是0
		*/

		/*将compareTo()改为return 1
			运行结果:
				xiaobai,12
				dabai,13
				xiaohei,14
				dahei,10
			按照存储的顺序输出
		*/

		/*将compareTo()改为return -1
			运行结果:
				dahei,10
				xiaohei,14
				dabai,13
				xiaobai,12
			按照存储的顺序逆序输出
		 */

	}
}

运行结果:
在这里插入图片描述
结论

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

比较器排序Comparator的使用

  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
//定义学生类
public class Student {
	private String name;
	private int age;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

}
/*
*比较器排序Comparator的使用**

- 存储学生对象并遍历,创建TreeSet集合使用**带参构造方法**
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo3 {
	public static void main(String[] args) {
		//创建集合对象
		TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {//使用匿名内部类
			@Override
			public int compare(Student s1, Student s2) {
				// TODO Auto-generated method stub
				//this.age--s.age
				//s1--s2
				int num = s1.getAge() - s2.getAge();
				int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
				return num2;
			}
		});

		//创建学生对象
		Student s1 = new Student("xiaobai", 12);
		Student s2 = new Student("dabai", 13);
		Student s3 = new Student("xiaohei", 14);
		Student s4 = new Student("dahei", 10);

		Student s5 = new Student("xiaohong", 10);
		Student s6 = new Student("xiaohong", 10);//元素唯一

		//学生添加到集合
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);

		//遍历集合
		for (Student s : ts) {
			System.out.println(s.getName() + "," + s.getAge());
		}

	}
}

运行结果:
在这里插入图片描述
结论:

  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

案例:成绩排序

需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
要求:按照总分从高到低出现

//定义学生类
public class Student {
	private String name;
	private int chinese;
	private int 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;
	}

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

	public Student() {
		super();
	}

	@Override
	public String toString() {
		return "name=" + name + ", chinese=" + chinese + ", math=" + math + ", sum=" + getSum();
	}

	public int getSum() {

		return this.chinese + this.math;
	}

}
import java.util.Comparator;
import java.util.TreeSet;

/*
案例:成绩排序
需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
要求:按照总分从高到低出现
*/
public class TreeSetDemo4 {
	public static void main(String[] args) {
		//创建集合对象
		TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
			@Override
			public int compare(Student s1, Student s2) {
				// TODO Auto-generated method stub
				//按照总分从高到低出现
				//s---this
				//s2---s1
				//			int num=(s2.getChinese()+s2.getMath())-(s1.getChinese()+s1.getMath());
				//主要条件
				int num = s2.getSum() - s1.getSum();
				//次要条件
				int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
				int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;

				return num3;

			}
		});

		//创建学生对象
		Student s1 = new Student("小白", 55, 66);
		Student s2 = new Student("小黑", 54, 76);
		Student s3 = new Student("小红", 67, 44);
		Student s4 = new Student("小黄", 23, 64);
		Student s5 = new Student("小蓝", 95, 78);

		Student s6 = new Student("小绿", 94, 79);//总分相同,单科成绩不同
		Student s7 = new Student("小紫", 94, 79);//总分和单科成绩都相同

		//学生添加到集合
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		//遍历集合
		for (Student s : ts) {
			System.out.println(s.toString());
		}

	}
}

运行结果:
在这里插入图片描述

案例:不重复的随机数

需求:编写一个程序,获取10个1–20之间的随机数,要求随机数不能重复,并在控制台输出

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

import hello.randomDemo;

/*
案例:不重复的随机数

需求:编写一个程序,获取10个1--20之间的随机数,要求随机数不能重复,并在控制台输出

*/
public class SetDemo2 {
	public static void main(String[] args) {
		//创建Set集合对象
		//		Set<Integer> set=new HashSet<Integer>();
		Set<Integer> set = new TreeSet<Integer>();

		//创建随机数对象
		Random r = new Random();

		//判断集合长度是否小于10
		while (set.size() < 10) {//小于10,就产生随机数添加到集合中
			int number = r.nextInt(20) + 1;
			set.add(number);
		}

		//变量集合
		for (Integer i : set) {
			System.out.println(i);
		}

	}
}

运行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值