【JavaSe】容器篇(四) Set集合

被刻印的时光 ゝ 提交于 2019-12-01 21:11:04

JavaSe·容器篇(四) Set集合


1. Set接口

java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set集合取出元素的方式可以采用:迭代器、增强for

2. HashSet集合

2.1 HashSet介绍

java.util.HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。 java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持

HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与 equals 方法。

public static void main(String[] args) {
	//创建 Set集合
	HashSet<String> set = new HashSet<String>();
	//添加元素
	set.add(new String("cba"));
	set.add("abc");
	set.add("bac");
	set.add("cba");
	//遍历
	for (String name : set) {
		System.out.println(name);
	}
}

输出:
cba
abc
bac

根据结果我们发现字符串"cba"只存储了一个,也就是说重复的元素set集合不存储。

2.2 HashSet集合存储数据的结构——哈希表

在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的:
在这里插入图片描述

2.3 HashSet存储原理图——即HashMap底层

在这里插入图片描述

总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
在这里插入图片描述

2.3 HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一

创建自定义类

public class Student {
	private String name;
	private int age;
	public Student() {}
	public Student(String name, int age) {
	this.name = name;
	this.age = 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;
	}
	@Override
	public boolean equals(Object o) {
		if (this == o)
		return true;
		if (o == null || getClass() != o.getClass())
		return false;
		Student student = (Student) o;
		return age == student.age && Objects.equals(name, student.name);
	}
	@Override
	public int hashCode() {
		return Objects.hash(name, age);
	}
}
public static void main(String[] args) {
	//创建集合对象 该集合中存储 Student类型对象
	HashSet<Student> stuSet = new HashSet<Student>();
	//存储
	Student stu = new Student("于谦", 43);
	stuSet.add(stu);
	stuSet.add(new Student("郭德纲", 44));
	stuSet.add(new Student("于谦", 43));
	stuSet.add(new Student("郭麒麟", 23));
	stuSet.add(stu);
	for (Student stu2 : stuSet) {
		System.out.println(stu2);
	}
}

执行结果:
Student [name=郭德纲, age=44]
Student [name=于谦, age=43]
Student [name=郭麒麟, age=23]

3. LinkedHashSet

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?在HashSet下面有一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。

public static void main(String[] args) {
	Set<String> set = new LinkedHashSet<String>();
	set.add("bbb");
	set.add("aaa");
	set.add("abc");
	set.add("bbc");
	Iterator<String> it = set.iterator();
	while (it.hasNext()) {
		System.out.println(it.next());
	}
}

4. TreeSet

TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

4.1 TreeSet特点

1)底层数据结构是红黑树,即平衡二叉树,有序(这里的有序不是list的有序概念),实现非同步,内部功能实现依赖于TreeMap的方法。

2)该类返回的元素顺序并非是集合添加元素的顺序,而是按照某个排序算法排列的。该算法有两种情况,由创建TreeSet实例时所用的构造函数决定使用哪种顺序。

4.2 TreeSet数据结构——红黑树

二叉树:binary tree ,是每个结点不超过2的有序树(tree)

简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点

二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”
在这里插入图片描述
我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。

在这里插入图片描述

4.2 TreeSet集合两种实现排序方式

  • 自然排序(元素具备比较性)
    TreeSet的无参构造,要求对象所属的类实现Comparable接口。

  • 比较器排序(集合具备比较性)

  • TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。

4.3 自然排序——Comparable

Comparable:

  • int compareTo(T o):比较此对象与指定对象的顺序。
class Student implements Comparable{ 	
	private String name; 	
	private int age; 
	Student(String name,int age){ 
		this.name=name; 
		this.age=age; 
	} 
	public int compareTo(Object obj){ 
		if(!(obj instanceof Student)){ 
			throw new RuntimeException("不是学生对象"); 
		} 
		Student s = (Student)obj; 
		int num = this.age-s.age; 
		if(num==0) {
			return this.name.comparTo(s.name); 
		} else {
			return num; 
		}
	} 
	// getter,setter..
	// toString..
} 
public static void main(String[] args){ 
	TreeSet ts =new TreeSet(); 
	ts.add(new Student("张三",21)); 
	ts.add(new Student("王五",23)); 
	ts.add(new Student("周七",19));
	ts.add(new Student("赵六",18));
	for(Iterator iter = ts.iterator();iter.hasNext();){ 
		System.out.println(obj); 
	} 
}

4.4 比较器排序——Comparator

Comparator:

  • int compare(T o1, T o2) : 比较用来排序的两个参数。
  • boolean equals(Object obj) :指示某个其他对象是否“等于”此 Comparator。
class Student { 	
	private String name; 	
	private int age; 
	Student(String name,int age){ 
		this.name=name; 
		this.age=age; 
	} 
	// getter,setter..
	// toString..
} 
class MyCompare implements Comparator{ 
	public int compare(Object obj1,Object obj2){ 
		Student s1 = (Student)obj1;
		Student s2 = (Student)obj2; 
		int num = new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())); 
		if(num==0) {
			return s1.getName().compareTo(s2.getName()); 
		}
		return num;
	} 
} 
public static void main(String[] args){ 
	TreeSet ts =new TreeSet(new MyCompare()); 
	ts.add(new Student("张三",21)); 
	ts.add(new Student("王五",23)); 
	ts.add(new Student("周七",19));
	ts.add(new Student("赵六",18));
	for(Iterator iter = ts.iterator();iter.hasNext();){ 
		System.out.println(obj); 
	} 
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!