MyException - 我的异常网
当前位置:我的异常网» 综合 » java集合框架详解(2)、set接口

java集合框架详解(2)、set接口

www.MyException.Cn  网友分享于:2015-06-09  浏览:0次
java集合框架详解(二)、set接口

3.1.2 Set接口

首先我们应该知道SetCollection接口的子接口

      SetCollection基本是一样的,但一点除外:Set无法记住添加的顺序,不允许包含重复的元素。一个不包含重复元素的 collection。更确切地讲,set 不包含满足e 1.equals(e 2)的元素对e 1e 2,并且最多包含一个 null 元素。

有几点说明:

     1、当试图添加两个相同元素进Set集合,添加操作会失败,add()方法返回false

     2Set判断两个对象是否相等用equals,而不是使用==

         也就是说两个对象equals比较返回trueSet集合是不会接受这个两个对象的。

我们从JDK API中看到Set接口的所有已知实现类有: 

AbstractSet, ConcurrentSkipListSet, CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet 

其中最常用的子类:aHashSet:散列存放      bTreeSet:有序存放

在详细介绍两个最长用子类之前,我们有必要明白HashSetTreeSet是两种不同的数据存放格式,对应着不同的存入数据方式和取数据方式

aHashSet:散列存放 

HashSet类是Set接口最常用的实现类,采用hash算法存储数据,具有良好的存储和查找功能。做如下几点说明:

    1、它散列存储,不记录添加顺序。

    2、排列顺序时,顺序有可能发生变化。同时线程不安全的,多个线程访问一个HashSet要使用同步代码。

    3、HashSet集合元素值允许是null,但是最多只能有一个;

    4hash(翻译为哈希,或散列)算法的功能:

    保证通过一个对象快速找到另一个对象,其算法价值体现在速度,可以保证查询快速执行

     当从HashSet中访问元素时,HashSet先计算该元素的hashCode(也就是该对象的hashCode方法返回值),然后直接到该HashCode对应的位置取出该元素。在这里对象的hashCode就好比是数组里的索引,但是不是索引。对于不同类型字段如何取得hashCode见下表(我们在程序实例中会说明如何在eclipse自动添加):

 

 

    接着讲述HashSet元素添加。 当向HashSet集合中存入一个元素时HashSet首先会调用该对象的hashCode()方法(是类Object的方法)来得到该对象的hashCode值,然后判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:

        若不一致:直接添加进去;

        若一致:再进行equals方法(是父接口Collection中的方法)比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去。

如果我们重写了equals方法,也要重写hashCode方法,反之亦然。

注意:

        1HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。

        2如果需要某个类的对象保存到HashSet集合中覆写该类的equals()hashCode()方法,应该尽量保证两个对象通过equals比较返回true,他们的hashCode返回也相等。

下表是HashSet类得方法列表:由于方法比较简单,我们将不会在程序中详细演示(可对照上篇中的方法演示)

 方法摘要

 boolean

add(E e) 
          如果此 set 中尚未包含指定元素,则添加指定元素。

 void

clear() 
          从此 set 中移除所有元素。

Object

clone() 
          返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。

boolean

contains(Object o) 
          如果此 set 包含指定元素,则返回 true。

boolean

isEmpty() 
          如果此 set 不包含任何元素,则返回 true。

Iterator<E>

iterator() 
          返回对此 set 中元素进行迭代的迭代器。

boolean

remove(Object o) 
          如果指定元素存在于此 set 中,则将其移除。

int

size() 
          返回此 set 中的元素的数量(set 的容量)。

 

我们来看第一个程序实例:参照代码理解上述HashSet的知识重点。

import java.util.HashSet;
import java.util.Set;

/**
 * HashSet添加元素:
 *		Set set = new HashSet();
 *		set.add("tyy");
 *			先调用"tyy".hashCode(),判断set集合里是否已经有了"tyy".hashCode():
 *			两种可能:	1.元素tyy的hashCode不存在:	 就直接把tyy添加到set集合;
 *					    2.元素tyy的hashCode存在:      再比较元素"tyy"和与那个与元素"tyy"具有一样的Hashcode值的元素对象,
								 使用对象的equals比较。比较结果有两张,分别是false与true,
								 false: 就添加进set;
								 true: 表明是一个对象,add(Object e),就返回false,添加失败
 */

//下面的方法hashCode()与eaquals()方法是用来对HashSet集中的元素进行判断的
//AA类仅仅覆写hashCode()方法
class AA{
	public int hashCode() {
		return 0;
	}
}
//BB类仅仅覆写equals(Object obj)方法
class BB{
	public boolean equals(Object obj) {
		return true;
	}
}
//CC类即覆写hashCode()同时也覆写equals(Object obj)
/*接着向HashSet中添加元素。 当向HashSet集合中存入一个元素时,HashSet首先会调用该对象(此时要添加的元素)的hashCode()方法(是
 * 类Object的方法)来得到该对象的hashCode值,然后判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:
 *					若不一致:直接添加进去;
 *					若一致:再进行equals方法(是父接口Collection中的方法)比较,equals方法如果返回true,表明对象已经添加进
 *去了,就不会再添加新的对象了,否则添加进去。如果我们重写了equals方法,也要重写hashCode方法,反之亦然。
*/

//如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法
class CC{
	public int hashCode() {
		return 10;
	}
	public boolean equals(Object obj) {
		return true;
	}
}
public class HashSetDemo {
	public static void main(String[] args) {
		
		
		Set set = new HashSet();//创建一个HashSet对象set
		boolean b = set.add("tyy");//添加一个元素tyy
		System.out.println(b);//true
		b = set.add("tyy");//再次添加元素tyy
		System.out.println(b);//false,这里是false的原因是,当添加两个相同元素进Set集合,由于他们两的hashCode相等
		//所以添加操作会失败,add()方法返回false。

		
		set.add(null);
		set.add(null);
		System.out.println(set);//[null, tyy] 。已经添加过一个null了,set集最多存储一个null
		
		set.clear();
		System.out.println("===================");
		set.add(new AA());
		set.add(new AA());
		set.add(new BB());
		set.add(new BB());
		set.add(new CC());
		set.add(new CC());
		System.out.println(set);
	}
}


本段程序的运行结果如下

true

false

[null, tyy]

===================

[collection.AA@0,collection.AA@0,collection.BB@e95a56,collection.BB@1632847collection.CC@a]

       我们分析一下结果,显然前四行的结果我们已在程序注释中给出解释了。56两行的结果原因是,在创建类元素AABB存储到hashSet集中时,要对元素进行hashCodeequals判断,而在类AABB中只是显示的覆写了两个判断方法中的一个,这也就是为什么AABB会类元素在结果中会出现两个,而CC只是一个。

      接着我们看下一个程序示例:

import java.util.HashSet;
import java.util.Set;


class Stu{
	private long id;
	private String name;
	
	//对学生的ID号进行hashCode与equals判断,以区别学生。
	//点击鼠标右键,选择源码,选择生产hashCode与equals,选择生产的字段,OK
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Stu other = (Stu) obj;
		if (id != other.id)
			return false;
		return true;
	}
	
}
public class HashSetDemo1 {
	public static void main(String[] args) {
		
		Set set = new HashSet();
		
		set.add(new Stu());
		
	}
}

bTreeSet:有序存放

这里我们进入JDK API文档,在Treeset类中,我们可以看到它有下面所示的构造方法:

TreeSet() 
     构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。

TreeSet(Comparator<? super E> comparator) 
          构造一个新的空 TreeSet,它根据指定比较器进行排序

     实际上它有四个构造方法,我们只看这两个。默认情况构造一个新的空set集时,该set集根据其元素的自然顺序进行排序(从小到大),就是构造方法一。而对于构造方法二,它是供我们自己设置排序方法的(比如我们可以设置成从大到小的顺序)

    一而言之:TreeSet类使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序。

注意:参与排序的元素必须是同一类型的,不然会发生ClassCastException异常。

看示例:TreeSetDemo1

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

public class TreeSetDemo1 {
	public static void main(String[] args) {

		
		//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
		Set set = new TreeSet();
		
		// 默认的比较就是 自然顺序,(升序)
        set.add("大哥");
		set.add("小姐");
		set.add("BB");
		set.add("AA");
		set.add("bb");
		set.add("aa");
		set.add("1");
		set.add("2");


		System.out.println(set);
		
	
	}
}

程序结果:[1, 2, AA, BB, aa, bb, 大哥, 小姐],可以看出它的输出顺序是什么样子的

TreeSetHashSet相比额外的方法有:

    Comparator comparator():返回当前Set使用的Comparator,若返回null,表示以自然顺序排序。

    Object first() 返回此 set 中当前第一个(最低)元素。 

    Object last() 返回此 set 中当前最后一个(最高)元素。 

    SortedSet subSet(Object  fromElement, E toElement) 返回此 set 的部子集,其元素从 fromElement(包括)到 toElement(不包括)。 

    SortedSet headSet(Object  toElement)返回此 set 的部分子集,其元素严格小于 toElement。 

    SortedSet tailSet(Object  fromElement) 返回此 set 的部分子集,其元素大于等于 fromElement。 

TreeSet的排序之自然排序

       TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合里的元素按升序排列。此时需要排序元素的类必须实现Compareble接口。并覆写其int compareTo(Object o)方法。

       compareTo(Object o)方法简介:接口Compareble中方法,比较此对象“this”与指定对象“o”的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

        对于TreeSet集合而言,判断两个对象相等的标准是: compareTo()方法比较返回 0

示例:

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

/**
 * 根据人的年龄来排序,升序
 * 
 */
class Person implements Comparable {
	private int age;
         // private Integer high;//high这里是引用数据类型
	public Person(int age) {
		this.age = age;
		//this.high= high;
	}

	public int compareTo(Object o) {
		if (o instanceof Person) {
			Person p = (Person) o; 
			if (this.age > p.age) {
				return 1;
			} else if (this.age < p.age) {
				return -1;
			}
		}
	   return 0;
		//如果排序字段是引用数据类型的话
		//if (o instanceof Person) {//判断o是否是属于Person
		//	Person p = (Person) o;
		//	return this.high.compareTo(p.high);
		//}
		//return 0;
	}

	public String toString() {
		return this.age + "";
	}
}

public class TreeSetDemo2 {
	public static void main(String[] args) {

		
		//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
		Set set = new TreeSet();
		/*
		 * 默认的比较就是 自然顺序,(升序);要排序必须实现java.lang.Comparable接口 再覆写该接口里的
		 * compareTo(Objec o){
		 */

		set.add(new Person(17));
		set.add(new Person(28));
		set.add(new Person(56));
		set.add(new Person(31));
		System.out.println(set);		
	
	}
}


程序运行结果:[17, 28, 31, 56]

TreeSet的排序之定制排序

       TreeSet的自然排序是根据元素的大小进行升序排序的,若想自己定制排序,比如降序排序,就可以使用Comparator接口(此接口是 Java Collections Framework 的成员)了。该接口包含int compare(Object o1Object o2)方法,用于比较两个对象的大小,比较结果和compareTo方法一致。

示例:

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


public class TreeSetDemo3 {
	public static void main(String[] args) {

		//构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
		Set set = new TreeSet();
		
		set.add(17);
		set.add(-2);
		set.add(95);
		set.add(4);
		TreeSet s   = (TreeSet)set;//set不能直接调用Comparator()方法,必须要先把set强转型为TreeSet类型
		/*                         
		 *  Comparator<? super E> comparator() 
          返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。 
		 * */
		System.out.println(s.comparator());
		System.out.println(s.first());
		System.out.println(s.last());
		/** 这里上下都是treeset类中的方法
		 *  SortedSet<E> headSet(E toElement) 
          			返回此 set 的部分视图,其元素严格小于 toElement。 
		 */
		System.out.println(s);
		System.out.println(s.headSet(17));
		System.out.println(s.tailSet(4));
	}
}

上述程序中的方法详细参见TreeSet类

程序运行结果:

-2

95

[-2, 4, 17, 95]

[-2, 4]

[4, 17, 95]

 

    要实现定制排序,需要在创建TreeSet集合对象时,提供一个Comparator对象(TreeSet(Comparator comparator) ),该对象里负责集合元素的排序逻辑   

示例:

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

/**
 * 根据人的年龄来排序,降序
 * 
 */
class Person2 {
	private Integer age;

	public Person2(int age) {
		this.age = age;
	}

	public Integer getAge() {
		return age;
	}

	public String toString() {
		return this.age + "";
	}
}

	class MyComparator implements Comparator{
		//在这里写比较规则
		public int compare(Object o1, Object o2) {
			Person2 p1 = (Person2) o1;
			Person2 p2 = (Person2) o2;
			
			if(p1.getAge() > p2.getAge()){
				return -1;
			}else if(p1.getAge() < p2.getAge()){
				return 1;
			}
			return 0;
		}
		
	}
public class ComparatorDemo {
	public static void main(String[] args) {
		Set set = new TreeSet(new Comparator() {//new TreeSet(new MyComparator());
			//在这里写比较规则,用的是匿名内部类
			public int compare(Object o1, Object o2) {
				Person2 p1 = (Person2) o1;
				Person2 p2 = (Person2) o2;
				
				if(p1.getAge() > p2.getAge()){
					return -1;
				}else if(p1.getAge() < p2.getAge()){
					return 1;
				}
				return 0;
			}
		});
		set.add(new Person2(23));
		set.add(new Person2(13));
		set.add(new Person2(03));
		set.add(new Person2(43));
		set.add(new Person2(93));
		System.out.println(set);
	}
}


 

 

 未完待续......

注:本文是参考众多资料,感谢前辈的支持。






文章评论

那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
总结2014中国互联网十大段子
总结2014中国互联网十大段子
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
如何成为一名黑客
如何成为一名黑客
中美印日四国程序员比较
中美印日四国程序员比较
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
10个调试和排错的小建议
10个调试和排错的小建议
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
那些争议最大的编程观点
那些争议最大的编程观点
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
 程序员的样子
程序员的样子
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
老程序员的下场
老程序员的下场
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员应该关注的一些事儿
程序员应该关注的一些事儿
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
漫画:程序员的工作
漫画:程序员的工作
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
我的丈夫是个程序员
我的丈夫是个程序员
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员必看的十大电影
程序员必看的十大电影
程序员都该阅读的书
程序员都该阅读的书
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
编程语言是女人
编程语言是女人
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员的鄙视链
程序员的鄙视链
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
一个程序员的时间管理
一个程序员的时间管理
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
鲜为人知的编程真相
鲜为人知的编程真相
我是如何打败拖延症的
我是如何打败拖延症的
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员和编码员之间的区别
程序员和编码员之间的区别
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
每天工作4小时的程序员
每天工作4小时的程序员
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有