21、集合之Set之hashCode和equals

Set是一个接口,继承了Collection

Set的一个实现类是HashSet

import java.util.HashSet;

public class SetTest2
{
	public static void main(String[] args)
	{
		HashSet set = new HashSet();
		
		set.add("A");
		set.add("B");
		set.add("C");
		set.add("D");
		
		System.out.println(set);
		//加进去四个,但是打印时与加入时的顺序不同,打印的顺序是随机的
		set.add(new People("zhangsan"));
		set.add(new People("lisi"));
		set.add(new People("zhangsan"));
		//三个都加进去了
		System.out.println(set);
		
		People p1 = new People("wangwu");
		set.add(p1);
		set.add(p1);//只能加进去一个
		
		String s1 = new String("a");
		String s2 = new String("a");
		set.add(s1);
		set.add(s2);//s2也加不进去
	}
}

class People
{
	String name;
	
	public People(String name)
	{
		this.name = name;
	}
}


对于Set的add方法,能否将元素加入集合中呢,查看add()方法的说明:

Adds the specified element tothis set if it is not already present.More firmally,adds the specified element e to this set if this set contains no element e2 such that

(e == null  ?  e2 == null  :  e.equals(e2)). If this set already contains the element,the call leaves the set unchanged and returns false.

如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲,如果此 set 没有包含满足 (e==null ? e2==null : e.equals(e2)) 的元素e2,则向此 set 添加指定的元素e。如果此 set 已包含该元素,则该调用不更改 set 并返回 false

1、在这里使用了equals方法,这个方法是Object类定义的,对于这个equals方法的特点

      a)自反性:It is reflexive: x.equals(x)应该返回true;

      b)对称性:It is symmetric:x.equals(y)为true,那么y.equals(x)也为true。

      c)传递性:It is transitive:x.equals(y)为 true并且y.equals(z)为true,那么x.equals(z)也应该为true。

      d)一致性:It is consistant:x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也应该为true,前提条件是在比较之间没有修改x也没有修改y。

      e)对于非空引用x,x.equals(null)返回false。

2、Object类的hashCode方法

      a) 在Java应用的一次执行过程当中,对于同一个对象的hashCode方法的多次调用,他们应该返回同样的值(前提是该对象的信息没有发生变化)——这里要注意是一次执行过程当中。两次执行中同一个对象的hashCode值可能不同,也可能相同

      b)对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode值一定是相同的。

      c)对于两个对象来说,如果使用equals方法比较返回false,那么这两个对象的hashCode值不要求一定不同(可以相同,可以不同),但是如果不同则可以提高应用的性能。

      d)对于Object类来说,不同的Object对象的hashCode值是不同的,hashCode返回值是一个整型值(Object类的hashCode值表示的是对象的地址)。

3、当使用HashSet时,hashCode()方法就会得到调用,判断已经存储在集合中的对象的hash code值是否与增加的对象的hash code值一致;如果不一致,直接加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。

因为判断是否可以加入set需要同时使用hashCode和equals方法,所以如果我们重写equals方法,那么也要重写hashCode方法,反之亦然。

4、对于上面的SetTest2类来说,因为People类没有重写hashCode和equals方法,所以

set.add(new People("zhangsan"));

set.add(new People("lisi"));

set.add(new People("zhangsan"));

两次new People("zhangsan")都加入了set,下面的程序重写了hashCode和equals方法,结果就只能加入一个

import java.util.HashSet;

public class SetTest3
{
	public static void main(String[] args)
	{
		HashSet set = new HashSet();
		
		Student s1 = new Student("zhangsan");
		Student s2 = new Student("zhangsan");
		
		set.add(s1);
		set.add(s2);
		//只能加入s1		
		System.out.println(set);
	}
}

class Student
{
	String name;
	
	public Student(String name)
	{
		this.name = name;
	}
	
	public int hashCode()
	{
		return this.name.hashCode();
	}
	
	public boolean equals(Object o)
	{
		if (this == o)
		{
			return true;
		}
		
		if(null != o && o instanceof Student)
		{
			Student s = (Student)o;
			if(name.equals(s.name))
			{
				return true;
			}
		}
		
		return false;
	}
}

Myeclips当中提供了自动生成hashCode和equals方法的机制,可以实现自动生成重写方法代码。

5、HashSet因为它里面的元素是无序的,也就是说,我们存进去的是一个顺序,取出来的时候是另一种顺序,或者多次取出时每次的顺序也不一样,就是说没有下标的概念,不能使用类似List的get(int index)方法取其中的元素,这时,HashSet提供了一个iterator方法——迭代器。对于HashSet的iterator()方法:

public Iterator<E> iterator()

Returns an iterator over the elements in this set.The elements are returned in no particular order.

返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的(无序的)。

返回的迭代器有三个方法:boolean hasNext(),Object next(),void remove()。

6、迭代器的使用:在通过迭代函数访问类集之前,必须得到一个迭代函数。每一个Collection类都提供一个iterator()函数,该函数返回一个对类集头的迭代函数。通过使用这个迭代函数对象,可以访问类集中的每一个元素,一次一个元素。通常,使用迭代函数循环通过类集的内容,步骤如下
      –1. 通过调用类集的iterator( )方法获得对类集头的迭代函数。
      –2. 建立一个调用hasNext( )方法的循环,只要hasNext( )返回true,就进行循环迭代。
      –3. 在循环内部,通过调用next( )方法来得到每一个元素

for(Iterator iter = set.iterator();iter.hasNext();)
		{
			String value = (String)iter.next();
			System.out.println(value);
		}


6、Set接口的子接口SortedSet,SortedSet的实现类为TreeSet。

TreeSet的输出不是任意的,她是按照一定规则进行排序后输出的有序序列。

import java.util.TreeSet;

public class TreeSetTest
{
	public static void main(String[] args)
	{
		TreeSet set = new TreeSet();
		
		set.add("C");
		set.add("A");
		set.add("E");
		set.add("D");
		set.add("B");
		
		
		System.out.println(set);
	}
}


这个输出为【A,B,C,D,E】,有序的。

如果我们加入的是自定义的类对象,如下:

import java.util.TreeSet;

public class TreeSetTest2
{
	public static void main(String[] args)
	{
		TreeSet set = new TreeSet();
		
		Person p1 = new Person(10);
		Person p2 = new Person(20);
		Person p3 = new Person(30);
		Person p4 = new Person(40);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		
		System.out.println(set);
	}
}

class Person
{
	int score;
	
	public Person(int score)
	{
		this.score = score;
	}
	
	public String toString()
	{
		return String.valueOf(this.score);
	}
}


运行时将出现错误:java.lang.ClassCastException。

查看TreeSet的add()方法,抛出ClassCastException是由于:如果指定对象无法与此 set 的当前元素进行比较。之所以TreeSetTest可以运行,是因为默认的TreeSet采用自然排序,也就会说如果set中的元素可自然排序(如对于字符串,按照自然排序也就是字典排序),如果不能采用自然排序,要么需要指定一个比较器,要么抛异常。

查看TreeSet的构造方法:

TreeSet()                                                                         构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Collection<? extends E> c)                          构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)       构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet(SortedSet<E> s)                                             构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。

第三种就是可以自己指定一个比较器。

假设我们已经有了一个比较器  Comparator myComparator,生成带特定比较器的TreeSet对象:

TreeSet set = new TreeSet(myComparator);

7、Comparator是一个接口,提供两个方法,int compare(T o1, T o2) 和equals()方法,我们需要实现Comparator接口的类并重写compare方法。

对于compare方法,用来比较排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。

 

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest2
{
	public static void main(String[] args)
	{
		TreeSet set = new TreeSet(new MyComparator1());//调用带有自定义比较器的构造方法
		
		Person p1 = new Person(10);
		Person p2 = new Person(20);
		Person p3 = new Person(30);
		Person p4 = new Person(40);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		for(Iterator iter = set.iterator();iter.hasNext();)
		{
			Person p = (Person)iter.next();
			System.out.println(p.score);
		}
		System.out.println(set);
	}
}

class Person
{
	int score;
	
	public Person(int score)
	{
		this.score = score;
	}
	
	public String toString()
	{
		return String.valueOf(this.score);
	}
}

class MyComparator1 implements Comparator                      //自定义比较器
{
	public int compare(Object arg0,Object arg1)
	{
		Person p1 = (Person)arg0;
		Person p2 = (Person)arg1;
		
		return p2.score - p1.score;
	}
}


 8、关于Collections类,类似于Arrays类,提供很多的静态方法,来对集合进行操作。

 Collections提供的常用方法:

<T> Comparator<T>  reverseOrder(Comparator<T> cmp)   返回一个比较器,它强行逆转指定比较器的顺序。
<T extends Comparable<? super T>> void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序。
static void shuffle(List<?> list)   使用默认随机源对指定列表进行置换。

min(),max()

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值