假如有个项目,要把一个公司的员工的名字,年龄,工资做一个表单,然后可以实现点按钮就按姓名笔画或者拼音,按工资水平,按年龄或者按其他方式进行排列,当然用数据库是最好的,但是如果要求用集合呢,个人认为TreeSet是个可以考虑的类,
set的特点:
不包含重复元素
Set集合没有索引(即没有顺序)
TreeSet的特点:
底层数据结构红黑树(red-black-tree)
存储到红黑树中的对象,会进行排序
线程不安全集合,运行速度快
引入一个set案例:
/*
* Set集合存储并迭代
* Set集合代码,和List集合代码,是一样的,但是set有自己的特点
* 不接受重复的元素,输出元素自然排序
*/
import java.util.*;
public class SetDemo {
public static void main(String[] args) {
method_2();
}
/*
* Set集合存储自定义对象Person,并迭代
* 将姓名和年龄,相同的对象去掉
*/
public static void method_2(){
Set<Person> set = newHashSet<Person>();
set.add(new Person("c2",182));
set.add(new Person("d4",184));
set.add(new Person("a2",182));
set.add(new Person("d4",183));
set.add(new Person("c2",182));
/*
输出:输出没有顺序,不会像treeSet那样自然排序
Person [name=a2, age=182]
//有重复的被砍掉了,如果字符相同,比较数字,这主要是调用了
//person类中自带的compareTo(Person p)方法
Person [name=d4, age=184]
Person [name=d4, age=183]
Person [name=c2, age=182]
*/
Iterator<Person> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
程序里引用了一个类Person泛型,Person是一个标准类实现了Comparable<Person>接口,这里将person类简单写下:
package bokebao;
public class Person implements Comparable<Person> {
. . . . . .
@Override返回了String字符串
public String toString() {
return"Person[name=" +name +", age=" +age +"]";
}
//重写了hashCode()方法
public int hashCode(){
returnname.hashCode()+age*31;
}
//重写了equals(Object obj)方法
public boolean equals(Object obj){
if(obj==null)
return false;
if(this==obj)
return false;
if(objinstanceof Person){
Person p=(Person)obj;
return this.name.equals(p.name)&&this.age==p.age;
}
return false;
}
@Override重写了compareTo(Person p)方法
public int compareTo(Person p) {
// TODO Auto-generatedmethod stub
int number=this.name.compareTo(p.name);
//三目运算符,如果姓名相同,比较年龄按照年龄进行顺序输出,
return number==0?this.age-p.age:number;
}
}
为什么在类中要加这么多重写还要实现接口呢?
这就是TreeSet使用类作为泛型的一个重点和难点,也是很多小伙伴困扰的地方,我们再调一个TreeSet案例看一下:
/*
* TreeSet集合存储对象并迭代
*/
import java.util.*;
public class TreeSetDemo {
public static void main(String[] args) {
method_2();
method_1();
}
/*
*TreeSet存储自定义对象Person,并迭代
* 排序是要使用比较器对象,不使用对象的自然顺序
* 比较器优先级最高
*/
public static void method_2(){
//在TreeSet构造方法中,传递写好的,比较器对象
Set<Person> set = new TreeSet<Person>(new CompareDemo());
//java.util 接口 Comparator<T>
set.add(new Person("zhangsan",18));
set.add(new Person("lisi",20));
set.add(new Person("wangwu",19));
set.add(new Person("zhaoliu",17));
for(Person p : set){
System.out.println(p);
}
}
/*
*TreeSet存储自定义对象Person,并迭代
* 排序使用的是对象的自然顺序
*/
public static void method_1(){
Set<Person> set = new TreeSet<Person>();
set.add(new Person("zhangsan",18));
set.add(new Person("wisi",20));
set.add(new Person("vangwu",19));
set.add(new Person("uhaoliu",17));
for(Person p : set){
System.out.println(p);
}
}
}
这个案例调用了两个方法,方法1没什么好说的,还是调用了Person类的重写的hashCode()和equals(Object obj)方法,如果类返回的字符串的哈希值相同,就用equals(Objectobj)比较字母或数字,注意几个方面
1、 Person必须实现接口Comparable否则在TreeSet中它会报错,类作为泛型
对象,必须重写接口中调用的方法,否则,TreeSet方法中默认的自然排序没办法排列,类里面不重写无法实现自然排序,这点一定要注意,你会发现程序写完了,Eclipse也没有报编译错误,就是运行出错。
2、 TreeSet要实现自然顺序排序,必然要调用内部的comparator
()
方法,
而comparator
()
指定者是通过接口SortedSet<E> 中的抽象方法comparator;
返回的一个是一个抽象的接口,要实现这个接口就只有在Person类中实现,所以在类中实现了java。Lang下的接口Comparable,这样Person就可以直接调用compareTo方法了,而主方法,也可以将调用的接口实例化
为了应付不同的要求排序方式我们还可以重写Comparator接口,这样就实现了TreeSet的快速存取的排序
/*
* 自定义的比较器对象
* 实现Comparator接口
*/
public class CompareDemoimplements java.util.Comparator<Person>{
/*
* p1和p2对象的年龄,年龄相同,在比较姓名
* 注意:p1对象的age-p2对象的age
* 比较器中,不允许直接使用Person类的私有成员,必须依靠get set
*/
public int compare(Person p1, Person p2) {
//计算两个对象的年龄差
int age = p1.getAge() - p2.getAge();
return age==0? p1.getName().compareTo(p2.getName()) :age;
}
}