Java Set接口详细讲解 TreeSet的定制排序和自然排序

Set接口概述

  1. Set接口是Collection的子接口,set接口没有提供额外的方法
  2. Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
  3. Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

Set实现类之一:HashSet

1 HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
2 HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。

HashSet 具有以下特点:

不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null

HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

Set实现类之一:HashSet

向HashSet中添加元素的过程:

1 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法
来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好)
2 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
在这里插入图片描述

重写 hashCode() 方法的基本原则

  • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
  • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
  • 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

重写 equals() 方法的基本原则

以自定义的Customer类为例,何时需要重写equals()?  当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是
要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”
结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

Set实现类之二:LinkedHashSet

  • LinkedHashSet 是 HashSet 的子类
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。

Set实现类之三:TreeSet

  • TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
  • TreeSet底层使用树结构存储数据
  • 新增的方法如下: (了解)
    1 Comparator comparator()
    2 Object first()
    3 Object last()
    4 Object lower(Object e)
    5 Object higher(Object e)
    6 SortedSet subSet(fromElement, toElement) 7SortedSet headSet(toElement)
    8SortedSettailSet(fromElement)
    TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序

排 序—自然排序

自然排序:TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。
向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添
加的所有元素都会调用compareTo()方法进行比较。
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。
当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。否则,让人难以理解。

排 序—定制排序

TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没
有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通Comparator接口来实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。
此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

package Java1;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/*
 * 向TreeSet中添加数据,要求是相同类的对象
 * 两种排序方式:自然排序(实现comparable接口)和定制排序(Comparator)
 * 
 * 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().
 */
public class TreeTest {
	public static void main(String[] args) {
		TreeSet set = new TreeSet();
//		set.add(123);
//		set.add(345);
//		set.add("afafda");
//		添加失败:不能添加不同类的对象
		
		//举例一
//		set.add(123);
//		set.add(0);
//		set.add(-8);
//		set.add(34);
//		set.add(76);
//		set.add(353);
		
		//举例二
		set.add(new User("tom",12));
		set.add(new User("tom",34));
		set.add(new User("eom",89));
		set.add(new User("adm",92));
		set.add(new User("ghm",16));
		
		Comparator com = new Comparator(){

			@Override
			public int compare(Object o1, Object o2) {
				if(o1 instanceof User&&o2 instanceof User){
					User u1  = (User)o1;
					User u2  = (User)o2;
					return Integer.compare(u1.getAge(), u2.getAge());
				}else{
					throw new RuntimeException("类型不匹配");
				}
			}
			
		};
		
		Iterator it = set.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}

User中的comareTo方法

//按照姓名从小到大排序
	@Override
	public int compareTo(Object o) {
		if(o instanceof User){
			User user = (User)o;
			//return this.name.compareTo(user.name);
			int compare = this.name.compareTo(user.name);
			if(compare!=0){
				return compare;
			}else{
				return Integer.compare(this.age,user.age);
			}
		}else{
			throw new RuntimeException("类型不匹配!!");
		}
	}
发布了70 篇原创文章 · 获赞 8 · 访问量 3270

猜你喜欢

转载自blog.csdn.net/gldbys/article/details/105076621
今日推荐