java TreeSet

TreeSet的特点

是可以对存放进去的元素进行排序,元素不重复

TreeSet实现了Set接口,元素不重复,只能存放引用数据类型,不能存放基本数据类型,底层是使用二叉树的原理,对新添加的对象按照指定的顺序排序(升序、降序),每增加一个对象都会进行排序,将对象插入的二叉树指定的位置。

      把一个对象添加进TreeSet时,该对象必须实现Comparable接口,TreeSet自动调用存储的对象的实现自Comparable接口的compareTo方法排序.如果对象没有实现Comparable接口,会报错.

TreeSet支持自然排序和定制排序,默认采用自然排序

1自然排序:

相同类型的对象元素,TreeSet调用该元素的compareTo(Object obj)方法比较元素之间大小,然后将元素按升序排列,

存储Integer类型数据 ,

Integer实现了Comparable接口 public final class Integer extends Number implements Comparable<Integer>

import java.util.Set;
import java.util.TreeSet;

public class TreeSetTest01 {
	public static void main(String[] args) {
		Set<Integer> set = new TreeSet<>();
		set.add(5);
		set.add(2);
		set.add(3);
		set.add(6);
		set.add(1);
		set.add(5);
		set.add(8);
		set.add(3);
		System.out.println(set);//[1, 2, 3, 5, 6, 8]
	}
}
out:
[1, 2, 3, 5, 6, 8]

存储自定义类型,没有实现Comparable接口

import java.util.Set;
import java.util.TreeSet;

import students.Student;

public class TreeSetTest02 {
	public static void main(String[] args) {
		Set<Student> set = new TreeSet<>();
		set.add(new Student(12, "张三"));
		set.add(new Student(11, "李四"));
		System.out.println(set);//类转型错误,cannot be cast to class java.lang.Comparable 报错,没有实现Coparable接口
	}
}
out:
Exception in thread "main" java.lang.ClassCastException: class students.Student cannot be cast to class java.lang.Comparable (students.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
	at java.base/java.util.TreeMap.put(TreeMap.java:536)
	at java.base/java.util.TreeSet.add(TreeSet.java:255)
	at set.TreeSetTest02.main(TreeSetTest02.java:11)

 存储自定义类型,实现Comparable接口,重写compareTo方法,重写的时候注意谁减去谁

扫描二维码关注公众号,回复: 4173319 查看本文章
修饰符(可有可无) class 类名 implements Comparable<类名>{类体}
public class Student1 implements Comparable<Student1>
重写compareTo方法
public int compareTo(引用类型 形参)
public int compareTo(Student1 s){}

  示例

public class Student1 implements Comparable<Student1> {

	private int age;
	private String name;

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

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student1 other = (Student1) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public int getAge() {
		return age;
	}

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

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "[age=" + age + ", name=" + name + "]";
	}

	// 重写接口的ComparaTo方法
	@Override
	public int compareTo(Student1 s) {
                //要注意谁-谁
		// 返回值等于0,表示两者相同,如果返回值大于0,表示对象本身比传入的对象大,如果返回值小于0,表示对象本身比传入的对象小
		// 先比较age
		int num = this.age - s.getAge();
		if (num != 0) {
			return num;
		} else {
			// 如果age一样,比较name
			return this.name.compareTo(s.getName());
		}
	}
}

测试类:
import java.util.Set;
import java.util.TreeSet;

import students.Student1;

public class TreeSetTest03 {
	public static void main(String[] args) {
		Set<Student1> set = new TreeSet<>();
		set.add(new Student1(12, "james"));
		set.add(new Student1(13, "jack"));
		set.add(new Student1(11, "tom"));
		set.add(new Student1(12, "luis"));
		System.out.println(set);
	}
}
out:
[[age=11, name=tom], [age=12, name=james], [age=12, name=luis], [age=13, name=jack]]

注意,如果字符串值为中文, 则不能进行汉语拼音排序,会不知道根据啥排序,因为String类中重写的compareTo方法是根据char类型对应的ascii值进行排序的。

compareTo方法的返回值,写成(this.成员变量-形参.成员变量)这种形式,如果为0,系统会认为两者一致,不会向集合Set中添加这个元素,如果大于0,将该元素存储到右边,如果小于0,将该元素存储到左边

java中实现了Comparable接口的常见类如下:

BigDecimal、BigIneger以及基本数据类型中数值类型对应的包装类:按它们对应的数值的大小进行比较。

Character:按字符的UNICODE值进行比较。

Boolean:true对应的包装类实例大于false对应的包装类实例。

String:按字符串中字符的UNICODE值进行比较。

Date、Time:后面的时间、日期比前面的时间、日期大。

注意:

将对象放入TreeSet时,重写该对象对应类的equals()方法时,equals方法和compareTo(Object obj)方法要有一致结果,例如equals返回true时,compareTo方法也要返回0,不然会出现存储错误

如果对存入TreeSet的对象的成员变量进行修改,如果compareTo方法中这个成员变量的判断处于前面,会导致它与其他对象的顺序发生变化,但是TreeSet不会再次对其排序,分两种情况,如果使用元素的引用去修改属性,修改后的元素还在Set中,可以删除

如果使用迭代器方式和用TreeSet的方法返回元素,对返回的元素修改其属性,会出现错误,contains方法返回值错误,remove方法也删除不了修改后的元素,但可以删除其他未修改的元素

示例:

//用迭代器返回的方式修改元素的属性
import java.util.Iterator;
import java.util.TreeSet;

import students.Student1;

public class TreeSetTest03 {
	public static void main(String[] args) {
		TreeSet<Student1> set = new TreeSet<>();
		Student1 s12 = new Student1(13, "jack");
		Student1 s11 = new Student1(12, "james");
		Student1 s13 = new Student1(11, "tom");
		Student1 s14 = new Student1(12, "luis");
		set.add(s12);
		set.add(s11);
		set.add(s13);
		set.add(s14);
		System.out.println(set);
		// 通过迭代器取出第一个元素,修改属性值
		Iterator<Student1> it = set.iterator();
		Student1 first = it.next();
		first.setAge(15);

		// 对TreeSet中存储的元素修改后,TreeSet不会重新排序
		System.out.println(set);

		// 测试TreeSet中是否还包含被修改后的对象
		System.out.println(set.contains(new Student1(15, "tom")));// false

		set.remove(new Student1(15, "tom"));// 删除不了,返回false
		System.out.println(set);

		set.remove(new Student1(12, "james"));// 可以删除属性值没修改的元素
		System.out.println(set);
	}
}
out:
[[age=11, name=tom], [age=12, name=james], [age=12, name=luis], [age=13, name=jack]]
[[age=15, name=tom], [age=12, name=james], [age=12, name=luis], [age=13, name=jack]]
false
[[age=15, name=tom], [age=12, name=james], [age=12, name=luis], [age=13, name=jack]]
[[age=15, name=tom], [age=12, name=luis], [age=13, name=jack]]
-------------------------------------------------------------------------
//用引用的方式修改元素的属性
package set;

import java.util.TreeSet;

import students.Student1;

public class TreeSetTest04 {
	public static void main(String[] args) {
		TreeSet<Student1> set = new TreeSet<>();
		Student1 s12 = new Student1(13, "jack");
		Student1 s11 = new Student1(12, "james");
		Student1 s13 = new Student1(11, "tom");
		Student1 s14 = new Student1(12, "luis");
		set.add(s12);
		set.add(s11);
		set.add(s13);
		set.add(s14);
		System.out.println(set);
//		set.first().setAge(20);//调用集合的方法得到
		// 元素再修改属性值出现的结果,跟使用迭代器返回元素然后再修改属性结果一样
		// 修改s11的属性值
		s11.setAge(20);
		System.out.println(set);
		// 判断Set是否还包含修改后的元素
		System.out.println(set.contains(new Student1(20, "james")));
		// 删除修改后的元素,可以删除
		set.remove(new Student1(20, "james"));
		System.out.println(set);
	}
}
out:
[[age=11, name=tom], [age=12, name=james], [age=12, name=luis], [age=13, name=jack]]
[[age=11, name=tom], [age=20, name=james], [age=12, name=luis], [age=13, name=jack]]
true
[[age=11, name=tom], [age=12, name=luis], [age=13, name=jack]]


2定制排序

Comparator接口中有个int compare(T o1, T o2)方法,用于比较o1和o2的大小,自定义类可以实现Comparator并重写它的方法

定制排序是TreeSet有个有参构造器。需要传入的参数是Comparator接口的实现类(定制的Comparator),如果传入了Comparator的子类对象(即上句说的定制的Comparator), 那么TreeSet就会按照传入对象中定制的规则排序.或者直接传入Comparator的对象,并重写它的compare方法,由该Comparator对象负责集合元素的排序逻辑.

1定制比较器,实现Comparator接口,并重写compare方法,实现接口时要指明泛型的类型,

import java.util.Comparator;

import students.Student1;

public class SortByNameLength implements Comparator<Student1> {

	@Override
	public int compare(Student1 o1, Student1 o2) {
//o1代表当前要存入的对象,o2是已存入的对象,实际是o1要和已存入的很多对象(o2)依次比较
		
		int num = 0;
		// 首先根据名字的长度进行排序,num>0说明o1比o2名字长度大
		num = o1.getName().length() - o2.getName().length();
		if (num == 0) {
			//按名字字符串比较,
			//现比较第一个字符,如相等,再比较第二个,依次比较
			//返回值大于0说明从左往右的o1的字符大于o2字符的ascii码
			num = o1.getName().compareTo(o2.getName());
			if (num == 0) {
				//如果名字完全一样了,就比较年龄
				num = o1.getAge() - o2.getAge();
			}
		}
		return num;//num值大于0,表示按照排序规则 o1比o2大
	}

}
测试类:
import java.util.TreeSet;

import students.Student1;

//
public class TreeSetTest05 {
	public static void main(String[] args) {
		TreeSet<Student1> set = new TreeSet<>(new SortByNameLength());
		set.add(new Student1(12, "ck"));
		set.add(new Student1(13, "jackson"));
		set.add(new Student1(14, "jack"));
		set.add(new Student1(13, "linda"));
		set.add(new Student1(12, "thomas"));
		System.out.println(set);
	}
}
out:
[[age=12, name=ck], [age=14, name=jack], [age=13, name=linda], [age=12, name=thomas], [age=13, name=jackson]]

compare(T o1,T o2)其中o1是刚传入的元素,o2是已存在的其他元素,升序就是o1-o2,降序就是o2-o1

2在TreeSet的构造方法中,传入Comparator接口的对象,并重写compare方法(如果TreeSet指明泛型类型,那么新创建Comparator接口对象时也要指明泛型类型,如果TreeSet没指明泛型,创建的Comparator对象也不用指明泛型,)

指明泛型类型的情况,Comparator对象也要指明泛型类型,如下

 将一个字符串中的字符按照字典顺序进行排序,要保留重复的字符。例如输入:java,打印aajv

分析:获取输入的字符串,转换为char数组,再利用TreeSet集合,将char数组放入TreeSet中,要排序,可以用TreeSet集合,但是Set是不重复的,要保留重复元素,就得实现Comparable接口并重写compareTo方法,但Charactor类是系统自带的,修改源码不太好,还有一种方法是创建Comparator的对象,传入构造方法中,实现自定义排序的TreeSet

package set;

//将一个字符串中的字符按照字典顺序进行排序,
//要保留重复的字符。例如输入:java,打印aajv
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

public class TreeSetTest06 {
	public static void main(String[] args) {
		// 定义扫描对象
		Scanner scanner = new Scanner(System.in);
		// 接收系统输入的字符串
		String str = scanner.next();
		// 将字符串转换为char数组
		char[] strArr = str.toCharArray();
		// 创建TreeSet对象,并定义比较方式
		TreeSet<Character> set = new TreeSet<>(new Comparator<Character>() {
			//重写创建Comparator对象的时候要重写compare方法,如果Tree指明了泛型种类,
			//创建Comparator对象时,也要指明泛型类型
			@Override
			public int compare(Character o1, Character o2) {
				int num = 0;
				num = o1.compareTo(o2);
				if (num == 0) {
					return 1;
				}
				return num;
			}
		});
		// 将char数组的元素依次添加进入TreeSet
		for (Character c : strArr) {
			set.add(c);
		} // set=[a, a, j, v]
			// 不换行打印,即可
//		for (Character c : set) {
//			System.out.print(c);
//		}
		// 将TreeSet转换为Charactor数组
		Character[] strsArr = set.toArray(new Character[set.size()]);
		String s = "";
		//对Character数组遍历,利用自动拆箱,将数组中元素拼接到字符串s上
		for (Character i : strsArr) {
			s += i;
		}
		System.out.println(s);
		
	}
}
out:
java
aajv

TreeSet不指明泛型时,构造方法中传入Comparator对象,实现compare方法时,o1和o2要强制转换成所要比较对象的类型,比如TreeSet虽没指明存放Student类,但实际存放Student类,compare方法也要根据Student类的属性来重写,首先Student s1=(Student)o1; Student s2=(Student)o2,再根据s1和s2的属性去重写compare方法

Student类中没有实现Comparable接口,也没有实现Comparator接口
import java.util.Comparator;
import java.util.TreeSet;

import students.Student;

public class TreeSetTest07 {
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void main(String[] args) {
		TreeSet set = new TreeSet(new Comparator() {

			@Override
			public int compare(Object o1, Object o2) {
				Student s1 = (Student) o1;
				Student s2 = (Student) o2;
				//先根据age排序
				int num = s1.getAge() - s2.getAge();
				//age一样时,再根据name排序
				if (num == 0) {
					// 调用了String类的compareTo方法
					num = s1.getName().compareTo(s2.getName());
				}
				//num=0表示Set中已存在此元素,不添加此元素
				return num;
			}
		});
		set.add(new Student(12, "james"));
		set.add(new Student(15, "tom"));
		set.add(new Student(12, "jackson"));
		set.add(new Student(13, "ck"));
		System.out.println(set);
	}
}

参考:http://www.cnblogs.com/lixiaolun/archive/2012/12/25/2832775.html

       http://www.monkey1024.com/javase/578

猜你喜欢

转载自blog.csdn.net/sinat_41132860/article/details/84202997
今日推荐