Java集合框架—Set


集合框架 Set的特点:无序,不可以重复元素。


(1)HashSet:数据结构是哈希表。线程是非同步的。
              保证元素唯一性的原理:判断元素的hashCode值是否相同。
              如果相同,还会继续判断元素的equals方法,是否为true。


(2)TreeSet:可以对Set集合中的元素进行排序。
               底层数据结构是二叉树。
               保证元素唯一性的依据:compareTo方法return 0。

一、HashSet相关知识

    HashSet中的元素不可以重复,如果重复添加,则只会显示一个。

原理如下:

HashSet:底层数据结构是哈希表。是线程不安全的。不同步。

 

HashSet是如何保证元素唯一性的呢?
答:是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashcode值不同,不会调用equals。

*******对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法**********


代码1:

package com.package2;

import java.util.*;

public class HashSet222 
{

	public static void main(String[] args) 
	{
		
		HashSet hs = new HashSet();

		hs.add("java01");
		hs.add("java01");
		hs.add("java02");
		hs.add("java03");
		hs.add("java03");
		hs.add("java04");

		Iterator it = hs.iterator();

		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}
此demo将String类型的字符串添加进去,并且没有重复,结果如下:

java04
java02
java03
java01

由此我们可以断定,String类已经实现了hashcode()方法和equals()方法。打开,帮助文档,确实(这不是废话么^_^)

但是,如果我们要将自定义的元素add进HashSet中,则必须定义其自己的hashcode()方法和equals()方法。如下所示:

代码2:

package com.package2;
import java.util.*;
public class HashSet3 
{
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();

		hs.add(new Person("a1",11));
		hs.add(new Person("a2",12));
		hs.add(new Person("a3",13));
		hs.add(new Person("a2",12));
		hs.add(new Person("a4",14));
		

		Iterator it = hs.iterator();

		while(it.hasNext())
		{
			Person p = (Person)it.next();
			System.out.println(p.getName()+"::"+p.getAge());
		}
	}
}
class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public int hashCode()
	{
		System.out.println(this.name+"....hashCode");
		return name.hashCode()+age*37;  //保证此元素的返回值尽量不一致。
	}

	public boolean equals(Object obj)
	{

		if(!(obj instanceof Person))
			return false;

		Person p = (Person)obj;
		System.out.println(this.name+"...equals.."+p.name);

		return this.name.equals(p.name) && this.age == p.age;
	}

	
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}
输出结果如下:

a1....hashCode
a2....hashCode
a3....hashCode
a2....hashCode
a2...equals..a2
a4....hashCode
a1::11
a3::13
a2::12
a4::14

由此可以看出,将元素add时,会首先调用元素的hashcode()方法,当返回值重复时,会调用其equals方法。缺少任何一种方法都构不成一个HashSet集合。



二、TreeSet相关知识

TreeSet有俩种排序方式。

TreeSet排序的第一种方式:让元素自身具备比较性。
步骤:将add进TreeSet中的元素实现Comparable接口,并且覆盖compareTo方法。这种顺序也是元素的自然顺序,或者叫做默认顺序。

TreeSet的第二种排序方式。当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性(即利用其另一种构造函数建立对象)。在集合初始化时,就有了比较方式。

步骤:利用某个指定类实现Comparator接口,并且覆盖compare()方法,则此类会成为一个具备比较方法的类。在建立TreeSet的时候,将此类对象传入其中。

则此时,添加进TreeSet中的元素可按照指定比较方法进行排序。


下边举例演示俩种排序方式。

排序方式一:代码1:

package com.package1;

import java.util.*;

public class TreeSettest {

	public static void main(String[] args) {
		//创建对象
		TreeSet ts=new TreeSet();
		
		//添加元素
		ts.add("abcjjj");
		ts.add("abb");
		ts.add("daccc");
		ts.add("gfg");
		ts.add("geee");
		ts.add("r");
		
		//进行迭代
		Iterator it=ts.iterator();
		
		//循环取出元素
		while(it.hasNext())
		{
			System.out.println(it.next());
		}

	}

}
输出结果如下:

abb
abcjjj
daccc
geee
gfg
r

由此可见,此时元素具备可比性,即按照其自然顺序进行排序。打开帮助文档,我们可以清晰的发现String已经实现了Comparable接口,并且已经覆盖了compareTo(),“这不是废话么^_^”,如图所示:




ps……如果我们要往TreeSet里添加的元素是自己刚刚定义的,我们也可以自己定义该元素的类实现Comparator接口,并且覆盖compareTo()方法,如下所示:代码2:

package com.package1;

import java.util.*;

class Student implements Comparable//该接口强制让学生具备比较性。
{
	private String name;
	private int age;

	Student(String name,int age)
	{
		this.name = name;
		this.age = age;
	}

	public int compareTo(Object obj)
	{

		//return 0;
		
		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象");
		Student s = (Student)obj;

		//System.out.println(this.name+"....compareto....."+s.name);
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;
		/**/
	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}
public class TreeSet2 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet();

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi02",21));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi007",29));
		//ts.add(new Student("lisi007",20));
		//ts.add(new Student("lisi01",40));

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

结果如下:

lisi06...18
lisi09...19
lisi007...20
lisi02...21
lisi02...22
lisi007...29

此例子将学生元素,强行按照年龄来排序,这就是我们想要的排序方式。


第二种排序如下:

还是刚刚代码1的例子,我们使用第二种排序方式,使得String类型的元素按照长度来排序。

代码3:

/*
 * 使元素按照长度来排序,若长度相同,则按照自然排序。
 */
public class TreeSettest {

	public static void main(String[] args) {
		//创建对象
		TreeSet ts=new TreeSet(new MyCompare());
		
		//添加元素
		ts.add("abc");
		ts.add("bcc");
		ts.add("das");
		ts.add("bcde");
		ts.add("asdfg");
		ts.add("befqfca");
		
		//进行迭代
		Iterator it=ts.iterator();
		
		//循环取出元素
		while(it.hasNext())
		{
			System.out.println(it.next());
		}

	}

}

//定义一个类实现Comparator接口,并且覆盖compare()方法。
class MyCompare implements Comparator
{

	@Override
	public int compare(Object o1, Object o2) {
		//进行强制类型转换
		String s1=(String) o1;
		String s2=(String) o2;
		
		//进行比较
		if(s1.length()>s2.length())
			return 1;
		if(s1.length()<s2.length())
			return -1;
		if(s1.length()==s2.length())
		{
			return s1.compareTo(s2);
		}
		return 0;
	}
}
结果如下:

abc
bcc
das
bcde
asdfg
befqfca

此种情况下,我们不方便修改源代码,而只需要修改比较方法时,我们就可以自己创建一个比较器。在建立TreeSet时,将比较器传入即可使元素按照特定比较方式输出。


总结:

Comparable(方式一)接口和Compartor(方式二)接口的比较:

 

         两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但

是需要修改源代码。

 

       用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把

比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其

可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。









posted on 2016-04-30 17:31  岚之山  阅读(135)  评论(0编辑  收藏  举报

导航