Set集合的排序
我们知道,Set集合是无序的,
可以使用TreeSet类,那么TreeSet进行排序的规则是怎样的呢?
1TreeSet支持两种排序方式,自然排序和定制排序,在默认情况下,TreeSet采用自然排序.
自然排序:
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合的元素按升序排列,这种方式就是自然排序.
为什么集合元素有compareTo方法,因为集合元素对象实现了Comparable接口,该方法返回一个整数值,当一个对象调用该方法与另一个对象进行比较,例如:
obj1.compareTo(obj2)如果返回0,表示这两个对象相等,如果该方法返回一个正整数,表示obj1大于obj2如果该方法返回一个负整数,表示obj1小于obj2
所以需要使用TreeSet集合进行自然排序,元素必须实现Comparable接口,但是Java一些常用的类已经实现了该接口,例如:String Character Boolean Date Time
BigDecimal BigInteger等
如:
-
TreeSet<String>ts=newTreeSet<String>();
-
ts.add("b");
-
ts.add("c");
-
ts.add("a");
-
System.out.println(ts);
我们也可以自定义集合元素:
-
TreeSet<Person>persons=newTreeSet<Person>();
-
persons.add(newPerson("abc",21));
-
persons.add(newPerson("efg",44));
-
persons.add(newPerson("hij",11));
-
persons.add(newPerson("klm",34));
-
System.err.println(persons);
-
for(Personperson:persons){
-
System.out.println(person.getName()+""+person.getAge());
-
}
Person类:
-
publicclassPersonimplementsComparable<Person>{
-
-
privateStringname;
-
privateintage;
-
-
publicPerson(Stringname,intage){
-
this.age=age;
-
this.name=name;
-
}
-
-
publicStringgetName(){
-
returnname;
-
}
-
-
publicvoidsetName(Stringname){
-
this.name=name;
-
}
-
-
publicintgetAge(){
-
returnage;
-
}
-
-
publicvoidsetAge(intage){
-
this.age=age;
-
}
-
-
publicintcompareTo(Persono){
-
if(this.age>o.age){
-
return1;
-
}elseif(this.age==o.age){
-
return0;
-
}else{
-
return-1;
-
}
-
-
}
-
-
@Override
-
publicStringtoString(){
-
return"name:"+this.name+"age:"+this.age;
-
}
-
-
@Override
-
publicinthashCode(){
-
finalintprime=31;
-
intresult=1;
-
result=prime*result+age;
-
result=prime*result+((name==null)?0:name.hashCode());
-
returnresult;
-
}
-
-
@Override
-
publicbooleanequals(Objectobj){
-
if(this==obj)
-
returntrue;
-
if(obj==null)
-
returnfalse;
-
if(getClass()!=obj.getClass())
-
returnfalse;
-
Personother=(Person)obj;
-
if(age!=other.age)
-
returnfalse;
-
if(name==null){
-
if(other.name!=null)
-
returnfalse;
-
}elseif(!name.equals(other.name))
-
returnfalse;
-
returntrue;
-
}
-
-
}
当我们把一个对象放入TreeSet中时,重写该对象对应类的equals方法,应该保证equals方法返回true,compareTo方法返回0.
如果两个对象通过compareTo(Object obj)方法比较返回0时,但他们通过equals方法比较返回false,这将导致TreeSet会将把这两个对象保存在不同的位置,从而两个对象都可以添加成功,这与Set集合的规则有点出入.
定制排序:
因为自然,默认按照升序排序,如果我们需要降序排列,我们可以通过Comparator接口.当然我们也可以通过上面的程序实现,只需要修改compareTo方法一段代码:
-
publicintcompareTo(Persono){
-
if(this.age>o.age){
-
return-1;
-
}elseif(this.age==o.age){
-
return0;
-
}else{
-
return1;
-
}
-
-
}
输出结果为:
现在我们来通过定制排序来实现降序排序:
-
TreeSet<Person2>person2s=newTreeSet<Person2>(newComparator<Person2>(){
-
publicintcompare(Person2o1,Person2o2){
-
if(o1.getAge()>o2.getAge()){
-
return-1;
-
}elseif(o1.getAge()==o2.getAge()){
-
return0;
-
}
-
return1;
-
}
-
});
-
person2s.add(newPerson2("aaa",12));
-
person2s.add(newPerson2("bbb",34));
-
person2s.add(newPerson2("ccc",10));
-
person2s.add(newPerson2("ddd",34));
-
for(Person2person:person2s){
-
System.out.println(person.getName()+""+person.getAge());
-
}
Person2:
-
publicclassPerson2{
-
privateStringname;
-
privateintage;
-
-
publicPerson2(Stringname,intage){
-
this.age=age;
-
this.name=name;
-
}
-
-
publicStringgetName(){
-
returnname;
-
}
-
-
publicvoidsetName(Stringname){
-
this.name=name;
-
}
-
-
publicintgetAge(){
-
returnage;
-
}
-
-
publicvoidsetAge(intage){
-
this.age=age;
-
}
-
}
输出结果:
我们发现定制排序的集合元素,不用实现Comparable接口.
Map集合排序:
2,TreeMap
与TreeSet集合类似的是,TreeMap也是基于红黑树对TreeMap中的所有key进行排序,从而保证TreeMap总所有的key-value处于有序状态,TreeMap也有两种方式排序:
自然排序:
所有的key必须实现Comparable接口,而且所有的key应该是同一个类,否则会出现类转换异常
定制排序:
创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有的key进行排序,采用定制排序kei不用实现Comparable接口.
例如:自然排序:
-
-
TreeMap<Person,String>map=newTreeMap<Person,String>();
-
map.put(newPerson("zhangsan",21),"90");
-
map.put(newPerson("lisi",33),"10");
-
map.put(newPerson("wangwu",22),"30");
-
map.put(newPerson("zhaoliu",55),"50");
-
-
for(Entry<Person,String>entry:map.entrySet()){
-
Personperson=entry.getKey();
-
System.out.println(person);
-
}
结果:
例如:定制排序:
-
TreeMap<Person2,String>map2=newTreeMap<Person2,String>(newComparator<Person2>(){
-
publicintcompare(Person2o1,Person2o2){
-
if(o1.getAge()>o2.getAge()){
-
return-1;
-
}elseif(o1.getAge()<o2.getAge()){
-
return1;
-
}
-
return0;
-
}
-
});
-
-
map2.put(newPerson2("aaa",21),"90");
-
map2.put(newPerson2("bbb",33),"10");
-
map2.put(newPerson2("ccc",22),"30");
-
map2.put(newPerson2("ddd",55),"50");
-
-
for(Person2key:map2.keySet()){
-
System.out.println(key.getName()+""+key.getAge());
-
}
我们知道TreeMap是按key排序如果想要安装value排序怎么办呢?
-
@SuppressWarnings("unchecked")
-
publicstaticMap.Entry<String,Integer>[]getSortedHashtableByValue(Mapmap){
-
Setset=map.entrySet();
-
Map.Entry<String,Integer>[]entries=(Map.Entry[])set
-
.toArray(newMap.Entry[set.size()]);
-
Arrays.sort(entries,newComparator(){
-
publicintcompare(Objectarg0,Objectarg1){
-
Longkey1=Long.valueOf(((Map.Entry)arg0).getValue()
-
.toString());
-
Longkey2=Long.valueOf(((Map.Entry)arg1).getValue()
-
.toString());
-
returnkey2.compareTo(key1);
-
}
-
});
-
returnentries;
-
}
-
Map<String,Integer>score=newHashMap<String,Integer>();
-
score.put("yzq",21);
-
score.put("yzh",23);
-
score.put("yhf",11);
-
score.put("ysq",45);
-
Map.Entry<String,Integer>[]entries=getSortedHashtableByValue(score);
-
for(Entry<String,Integer>entry:entries){
-
Stringkey=entry.getKey();
-
Integervalue=entry.getValue();
-
System.out.println(key+":"+value);
-
}
输出结果:
-------hashCode()方法的作用------
我们知道set集合的元素是不能重复的,它内部是通过equals方法来比较的,如果这两个元素的equals()方法返回true,那么Set集合就认为这两个元素是一样的,就不会往集合里添加两次.(注意:在没有对equals(Object)方法做任何手脚的情况下,equals(Object)和"=="操作符是同样的通能的,我们打开Object的equals方法源代码发现equals方法里面的判断方法依然是通过"=="来判断.但是如果比较两个String值相等的对象时虽然通过new关键字新建了两个对象,但是他们的equals方法却返回真,因为在String中他已经重写了equals方法了,也就是说在String类中它已经对equals方法进行了修改)
我们来看一个程序:
-
publicclassHashCodeTest{
-
-
publicstaticvoidmain(String[]args){
-
-
Setstr=newHashSet();
-
str.add("a");
-
str.add("a");
-
System.out.println(str.size());
-
-
-
Setcats=newHashSet();
-
Catc1=newCat("jetty",3);
-
Catc2=newCat("jetty",3);
-
Catc3=newCat("petty",2);
-
cats.add(c1);
-
cats.add(c2);
-
-
cats.add(c2);
-
cats.add(c3);
-
System.out.println(cats.size());
-
-
}
-
-
}
-
classCat{
-
privateStringname;
-
privateintage;
-
-
-
publicCat(Stringname,intage){
-
super();
-
this.age=age;
-
this.name=name;
-
}
-
publicStringgetName(){
-
returnname;
-
}
-
publicvoidsetName(Stringname){
-
this.name=name;
-
}
-
publicintgetAge(){
-
returnage;
-
}
-
publicvoidsetAge(intage){
-
this.age=age;
-
}
-
}
哈希集合HashSet是Set集合的子类,既然set集合判断元素是通过equals方法,那么我们就让Cat类来覆写Object的equals方法:
-
@Override
-
publicbooleanequals(Objectobj){
-
if(this==obj)
-
returntrue;
-
if(obj==null)
-
returnfalse;
-
if(getClass()!=obj.getClass())
-
returnfalse;
-
Catother=(Cat)obj;
-
if(age!=other.age)
-
returnfalse;
-
if(name==null){
-
if(other.name!=null)
-
returnfalse;
-
}elseif(!name.equals(other.name))
-
returnfalse;
-
returntrue;
-
}
从输出结果我们发现,结果依然是3, c1.equals(c2)返回true,集合就不会把重复的元素放进的,但是为什么结果返回3呢?
现在我们就在Cat类中覆写hashCode方法:
-
@Override
-
publicinthashCode(){
-
finalintprime=31;
-
intresult=1;
-
result=prime*result+age;
-
result=prime*result+((name==null)?0:name.hashCode());
-
returnresult;
-
}
发现结果是2,所以对于哈希集合,我们要注意hashCode方法.
注意:如果我们覆写了hashCode方法,我们就要指定按照这个对象的哪些属性来计算这个对象的hashCode值,如果我们一旦选择了某个属性作为计算hashCode值,我们把这个对象保存到集合中去后,我们最好不要修改该对象的参与计算hashCode值的属性,这样容易导致内存泄漏如:
-
Setcats=newHashSet();
-
Catc1=newCat("jetty",3);
-
Catc2=newCat("jetty",3);
-
Catc3=newCat("petty",2);
-
cats.add(c1);
-
cats.add(c2);
-
cats.add(c3);
-
c2.setName("baby");
-
cats.remove(c2);
-
System.out.println(cats.size());
发现我们没有成功删除,
然后我们把修改属性的代码注释,发现就成功删除了,
因为HashSet集合是通过hashCode值来决定元素的存放位置,我们已经修改了c2的name属性,那么它的hashCode值就和以前的不同了,然后再根据新的hashCode值去删除未修改前的对象,发现找不到这样的hashCode值,所以就无法删除,从而导致内存泄漏.
分享到:
相关推荐
1.概述 2.为什么重写equels方法要重写hashcode方法 3.例子
Java equals 方法与hashcode 方法的深入解析.rar
如果一个类的hashCode()方法没有遵循上述要求,那么,当这个类的两个实例对象用equals()方法比较的结果相等时,他们本来应该无法被同时存储进set集合
本篇文章详细介绍了Java中的equals和hashCode方法详解,Object 类是所有类的父类,非常具有实用价值,需要的朋友可以参考下。
主要给大家介绍了关于Java中HashCode方法的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...
重写equals和hashcode方法,学习和进步
Java重写equals同时需要重写hashCode的代码说明,以及如何重写hashCode方法,此代码演示按照effective java书籍说明的重写思路。代码中演示了使用集合存储对象,并且对象作为key,需重写equals和hashCode.
HashMap的应用实例 ...2,当向HashMap中put一对键值时,它会根据key的hashCode值计算出一个位置,该位置就是此对象准备往数组中存放的位置。 HashMap应用举例:控制台输入一句英语,简单统计各个单词出现的次数
更清楚的了解hashcode()和equals()方法。
Java容器集合(equals 和 hashCode+基础数据结构+ArrayList+Vector和LinkedList)
深入 HashCode 方法~~~~~
有许多人学了很长时间的Java,但一直不明白hashCode方法的作用以及equals()和==的区别,我来解释一下吧。首先,想要明白hashCode的作用,你必须要先知道Java中的集合。总的来说,Java中的集合(Collection)有两类,...
java中hashcode和equals的详解.pdf
实际上,hashcode根本不能代表object的内存地址。
主要介绍了Java重写equals及hashcode方法流程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
本文中详细的阐述了Java中经常遇到的equals、hashcode以及“==”号三者之间的区别
本篇文章主要介绍了Java中的hashcode方法,详细的介绍了hashCode方法的作用,具有一定的参考价值,有需要的可以了解一下。
java 序列化和重写 hashCode 以及 equals 方法的例子
注意:因为Person类是自定义类,需要重写hashCode()方法和equals()方法,并规定只有姓名和身份证号都相等,则对象相等。 其中计算哈希码的算法:(31 + ((name == null) ? 0 : name.hashCode()))*31 + id (注:...