Java TreeSet的两种自定义类比较方式及TreeSet存储原理

自定义排序与TreeSet使用详解
这篇博客详细介绍了如何在Java中使用TreeSet进行自定义排序。内容涵盖了当元素具备自然顺序特性和不具此特性时如何添加到TreeSet中,以及如何通过实现Comparable接口或提供Comparator来定义比较规则。示例展示了Emp1类实现Comparable接口进行排序,以及Emp2类通过MyComparator比较器进行排序。最后,代码展示了两种方式下TreeSet的元素输出效果。
package com.heilong.collection;

import java.util.Comparator;
import java.util.TreeSet;
/*
 * TreeSet添加自定义元素:
 * 
 * TreeSet要注意的事项:
 * 1.往TreeSet添加元素的时候,如果元素本身具备了自然元素的特性,那么就按照元素自然顺序的特性进行排序存储
 * 2.往TreeSet添加元素的时候,如果元素本身不具备自然元素的特性,那么该元素所属的类必须要实现Comparable接口,把元素的
 * 		比较规则定义在compaerTo(T o)方法内
 * 3.如果比较元素的时候,compareTo方法返回的是0,那么该元素视为重复元素,不允许添加
 * 4.往TreeSet添加元素的时候,如果元素本身没有具备自然顺序的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器
 * 5.往TreeSet添加元素的时候,如果元素本身没有具备自然顺序的特性,元素所属的类实现了Comparable接口,在创建TreeSet的时候也传入了一个比较器,那么是以比较器的比较规则优先
 * 
 * 如何自定义比较器:自定义一个类实现Comparator接口即可,把比较规则定义在compara()方法内。
 * 
 * 		class 类名 implements Comparator{
 * 		
 * 		}
 * */

//方式一:
class Emp1 implements Comparable<Emp1>{
	
	int id;
	
	String name;
	
	int salary;
	
	public Emp1(int id, String name, int salary){
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {

		return "{ 编号:" + this.id + " 姓名:" + this.name + " 薪水:" + this.salary;
	}
	
	@Override
	public int compareTo(Emp1 e) {
		System.out.println(this.name + "cpmpare" + e.name);
		return this.salary-e.salary;
	}
}

//方式二:



class Emp2 {
	
	int id;
	
	String name;
	
	int salary;
	
	public Emp2(int id, String name, int salary){
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {

		return "{ 编号:" + this.id + " 姓名:" + this.name + " 薪水:" + this.salary;
	}
	
}

class MyComparator implements Comparator<Emp2>{

	@Override
	public int compare(Emp2 e1, Emp2 e2) {
		return e1.salary-e2.salary;
	}
	
	
}

public class treeSet {

	public static void main(String[] args) {
		TreeSet treeSet1 = new TreeSet();
		
		treeSet1.add(new Emp1(001,"张三",200));
		treeSet1.add(new Emp1(002,"李四",300));
		treeSet1.add(new Emp1(003,"王五",100));
		treeSet1.add(new Emp1(004,"赵六",500));
		
		System.out.println(treeSet1);
		
		System.out.println("******************************************");
		
		MyComparator myComparator = new MyComparator();
		TreeSet treeSet2 = new TreeSet(myComparator);
		
		treeSet2.add(new Emp2(001,"张三",200));
		treeSet2.add(new Emp2(002,"李四",300));
		treeSet2.add(new Emp2(003,"王五",100));
		treeSet2.add(new Emp2(004,"赵六",500));
		
		System.out.println(treeSet2);
	}
}

在这里插入图片描述

<think>首先,用户的问题是:“我想查找TreeSet集合进行比较两种方式 请问TreeSet集合进行比较两种方式”。用户想了解TreeSet集合进行比较两种方式。 从系统级指令中,我知道我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识。 用户提供了三个引用: - 引用[1]: 提到TreeSet集合以及两种排序:比较器Comparator的使用,使用TreeSet集合存储自定义对象时,通过构造方法接受Comparator的实现类对象。 - 引用[2]: 提到TreeSet集合两种比较器排序:Comparable和Comparator。实现步骤包括使用比较器排序,让构造方法接受Comparator的实现类对象。 - 引用[3]: 提到JAVATreeSet集合的两种排序方法:自然排序和比较器排序。自然排序需要元素实现Comparable接口比较器排序需要提供比较器对象。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,这是第一个用户消息,所以没有“上一次的问题”。用户可能指的是之前的上下文,但这里是新的对话启动。所以,我应该专注于当前问题和提供的引用。 用户真正的需求是查找TreeSet集合进行比较两种方式。从引用中,我可以总结出两种方式: 1. **自然排序 (Natural Ordering)**:元素实现Comparable接口。 2. **比较器排序 (Comparator Sorting)**:通过提供Comparator的实现类对象。 在回答中,我需要结构化: - 解释两种方式。 - 给出代码示例来演示。 - 确保回答清晰、逐步解决问题。 - 在结束时生成相关问题。 回答结构: 1. **介绍TreeSet**:简要解释TreeSet是什么以及为什么需要排序机制。 2. **解释两种比较方式**: - 自然排序:元素实现Comparable接口。 - 比较器排序:使用Comparator接口。 3. **代码示例**:用Java代码展示如何实现这两种方式。 4. **总结**:强调区别和用法。 5. **生成相关问题**:基于主题生成2-3个相关问题。 使用中文回答。 引用标识:在回答中,如果引用提供的引用,在末尾添加引用标识如[^1],但要自然地融入。 提供的引用是[1], [2], [3],所以我需要在相关内容后添加它们。 例如: - 当提到自然排序和比较器排序时,引用[^3]。 - 当提到实现步骤时,引用[^1]或[^2]。 但用户说“上述引用仅供参考”,所以我应该主要基于知识,但参考它们来支持。 现在,生成相关问题: - 指令是“生成相关问题”,在示例中是“§§相关问题§§”。 - 生成2-3个相关问题,如: - TreeSet的自然排序是如何工作的? - 如何在TreeSet中使用自定义比较器? - TreeSet的排序性能如何? 确保LaTeX:虽然主题是编程,但如果有数学表达式,使用$...$,但在这里可能不需要,因为它是Java集合。如果有变量,比如在解释中,但可能不必要。 回答大纲: - 开头:直接回答用户的查询。 - 主体: - 解释自然排序。 - 解释比较器排序。 - 代码示例。 - 结尾:相关问题。 </think>### TreeSet集合进行比较两种方式 TreeSetJava中的一种有序集合(基于红黑树实现),它要求元素必须可排序,否则会抛出ClassCastException。TreeSet元素的排序主要通过两种方式实现:**自然排序(Natural Ordering)** 和 **比较器排序(Comparator Sorting)**[^3]。下面我将逐步解释这两种方式,包括它们的原理、实现步骤和代码示例。 #### 1. 自然排序(Natural Ordering) 自然排序要求集合中的元素必须实现`Comparable`接口,并重写`compareTo(T o)`方法。该方法定义了元素的默认排序规则:例如,对于整数或字符串,TreeSet会使用内置的自然顺序(如升序)。如果元素是自定义对象(如Person类),则必须在类中实现`Comparable`接口来指定排序逻辑[^3]。 **实现步骤**: - 步骤1: 定义自定义类并实现`Comparable`接口。 - 步骤2: 重写`compareTo(T o)`方法,在其中编写排序规则。 - 步骤3: 创建TreeSet对象使用无参构造方法,TreeSet会自动使用元素的自然排序。 **代码示例**: 假设有一个Person类,要求按年龄升序排序。如果年龄相同,则按姓名排序(次要条件)。 ```java import java.util.TreeSet; // 自定义类实现Comparable接口 class Person implements Comparable<Person> { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } // 重写compareTo方法定义排序规则 @Override public int compareTo(Person other) { // 主条件: 按年龄升序 if (this.age != other.age) { return this.age - other.age; } // 次条件: 年龄相同则按姓名升序 return this.name.compareTo(other.name); } @Override public String toString() { return name + "(" + age + ")"; } } public class Main { public static void main(String[] args) { TreeSet<Person> set = new TreeSet<>(); // 无参构造,使用自然排序 set.add(new Person("Alice", 25)); set.add(new Person("Bob", 20)); set.add(new Person("Charlie", 25)); // 年龄相同,按姓名排序 System.out.println(set); // 输出: [Bob(20), Alice(25), Charlie(25)] } } ``` 在这个示例中,Person类实现了`Comparable`接口,`compareTo`方法定义了主次排序条件。TreeSet会自动调用该方法进行排序[^3]。 #### 2. 比较器排序(Comparator Sorting) 比较器排序不要求元素实现`Comparable`接口,而是通过外部比较器(实现`Comparator`接口的对象)定义排序规则。这种方式更灵活,允许多种排序策略(如降序或自定义字段排序),而不改变元素类本身[^1][^2]。TreeSet的带参构造方法(如`TreeSet(Comparator<? super E> comparator)`)接受一个Comparator对象。 **实现步骤**: - 步骤1: 创建Comparator的实现类(或使用匿名内部类),并重写`compare(T o1, T o2)`方法。 - 步骤2: 在方法中定义排序规则,注意处理主次条件(如先按成绩降序,再按学号升序)。 - 步骤3: 创建TreeSet对象时传入Comparator对象。 **代码示例**: 同样以Person类为例,但这次使用比较器实现按姓名降序排序。 ```java import java.util.Comparator; import java.util.TreeSet; class Person { // 不需要实现Comparable String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name + "(" + age + ")"; } } public class Main { public static void main(String[] args) { // 创建Comparator实现类 Comparator<Person> nameComparator = new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { // 主条件: 按姓名降序 return p2.name.compareTo(p1.name); // 反转自然顺序实现降序 } }; TreeSet<Person> set = new TreeSet<>(nameComparator); // 带参构造,传入比较器 set.add(new Person("Alice", 25)); set.add(new Person("Bob", 20)); set.add(new Person("Charlie", 25)); System.out.println(set); // 输出: [Charlie(25), Bob(20), Alice(25)] } } ``` 在此示例中,我们使用了匿名内部类实现Comparator,并传入TreeSet的构造方法。这允许在不修改Person类的情况下改变排序行为[^1][^2]。 #### 关键区别与总结 - **自然排序**:元素必须实现Comparable接口,适用于固定默认排序。优点是简单直接,缺点是排序规则绑定到元素类。 - **比较器排序**:使用外部Comparator对象,适用于动态或多变排序场景。优点是灵活(如可定义多个比较器),缺点是代码稍复杂。 - 注意:如果同时使用两种方式(元素实现Comparable且提供Comparator),TreeSet会优先使用Comparator排序[^3]。排序规则必须严格遵循一致性(如避免返回0时误判相等),否则可能导致集合行为异常。 通过以上两种方式TreeSet能高效地维护元素顺序(时间复杂度为$O(\log n)$),适用于需要有序存储的场景(如排行榜或字典)[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值