java 集合类 之 Set
2014-03-26 14:19
253 查看
Set类
Set接口继承自Collection,不按特定的方式排序,并且不能重复,主要有两个实现类:HashSet和TreeSet,HashSet类按照哈希算法来存取集合中对象,存取速度比较快,HashSet类还有一个子类LinkedHashSet类,不仅实现了哈希算法,而且实现了链表数据结构,提高数据结构的插入和删除元素的性能,TreeSet类实现了SortedSet接口,具有排序功能。
看下这个程序:
把s2 的对象引用赋值给s1,则2个位同一个引用,值是相同的。Set采用equals()比较对象是否相等。而不是“==”。
HastSet类
我们先从HashSet源码看起:
下面是HashSet的定义:
HashSet继承了AbstractSet,实现了Set接口。其实AbstractSet已经实现Set接口了。AbstractSet继承自AbstractCollection,而AbstractCollection实现了Collection接口的部分方法,而Set接口和Collection接口完全一致,所以AbstractSet只是实现了AbstractCollection没有实现的Set接口的方法和重写了部分AbstractCollection已经实现的方法。AbstractSet实现了equql()、hashCode()、removeAll()功能的重写。
再看HashSet
下面是HashSet定义的属性:
想一下HashMap有什么特点:基于哈希表,存储键值对,Key不能相同等等。Key不能相同!这个特点是不是和Set的元素不能相同和类似?如果将Set的元素当成Map的Key,是否就保证了元素的不重复?!答案是肯定的。但是Map存储键值对,Key有了,那么Value呢?这正是第二个属性PERSENT的意义。看到PERSENT属性时一个Object对象,且是static和final的,它的用途就是当做Value存进map中。
总结一下,HashSet的实现方式大致如下,通过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。
这样看来HashSet应该很简单,应该只是使用了HashMap的部分内容来实现。
看下add()方法源码 ,即是通过HashMap实现,
public
boolean add(E e) {
returnmap.put(e,PRESENT)==null;
}
addAll方式是在抽象类AbstractCollection中实现,HashSet通过继承拥有该方法,采用循环插入实现。
很清楚了,返回的是HashMap中KeySet的迭代器。
size()
public int size() {
return
map.size();
}
size()方法同样返回的是map的大小,所以HashSet根本就没定义size属性。
public boolean isEmpty() {
return map.isEmpty();
}
既然size()用的是map的大小,那么isEmpty()自然也是判断map。
这几个方法就不解释了。
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
remove(Object o)为什么还要判断结果呢?因为通过HashSet存入的元素,所对应的Value值都是PERSENT,如果传入的o不存在,map的remove方法返回为null,则对应的结果是HashSet的remove操作应该放回false,所以这里根据返回的结果判断是否移除成功。
在Object类中定义了hashCode()和equals()方法,equals()方法按照内存地址比较对象是否相等,因此如果object1.equals(object2)为true,则表明两个变量引用同一个对象,其哈希码也肯定相等。如果用户定义一个类覆盖了Object类的equals()方法,但没有覆盖Object类的hashCode()方法,就会会导致,当object1.equals(object2)为true,而哈希码不一定一样,导致HashSet或HashMap无法正常工作。
package com.bin.proxy;
class Customer {
String name;
int
age;
public String getName() {
return
name;
}
public
void setName(String name) {
this.name = name;
}
public
int getAge() {
return
age;
}
public
void setAge(int age) {
this.age = age;
}
public
boolean equals(Object o){
if(this ==o)returntrue;
if((o
instanceof Test5))returnfalse;
final Customer test=(Customer)o;
if(this.name.equals(test.getName())&&this.age==test.getAge())
return
true;
else
return
false;
}
public
int hashCode(){
int result;
result=(name ==null?0:name.hashCode());
result = 29*result+age;
return result;
}
}
public
class Test5{
public
static void main(String[] args){
Customer t1= new Customer();
Customer t2= new Customer();
t1.setAge(4);
t1.setName("song");
t2.setAge(4);
t2.setName("song");
System.out.print(t1.equals(t2));
}
}
如上程序,若把hashCode()去掉,则在HashSet和HashMap代表不同的对象。
四、TreeSet类
其构造方法为:
例程:
Set<Integer>set =new TreeSet<Integer>();
set.add(newInteger(8));
set.add(new Integer(7));
set.add(new Integer(9));
则程序打印set时显示 6 7 8 9。
TreeSet支持两种排序方式:自然排序和客户化排序,默认采用自然排序
1、 自然排序
在JDK中有个Comparable接口,部分类实现了他,如Integer,Double和String等,其有个接口compareTo(Object o)方法,x.compareTo(y),返回0,表示相等,若大于0表示x大于y,反之小于0,表示x小于y。
TreeSet调用compareTo()方法比较集合对象的大小,然后进行升序排序,成为自然排序,使用自然排序的时候,只能想TreeSetJ集合加入同类型的对象,这些对象必须实现了Comparable接口,
2、 客户化排序
除了自然排序,TreeSet还支持客户化排序,java.util.Comparator<Type>接口提供具体排序方式,Comparator有个compare(Type x,Type y)方法,用于比较对象大小,当返回大于0时,表示x大于y,当放回小于0时,表示x小于y,当返回等于0时,表示x等于y
Set接口继承自Collection,不按特定的方式排序,并且不能重复,主要有两个实现类:HashSet和TreeSet,HashSet类按照哈希算法来存取集合中对象,存取速度比较快,HashSet类还有一个子类LinkedHashSet类,不仅实现了哈希算法,而且实现了链表数据结构,提高数据结构的插入和删除元素的性能,TreeSet类实现了SortedSet接口,具有排序功能。
看下这个程序:
package com.bin.proxy; import java.util.HashSet; import java.util.Set; public class Test4 { public static void main(String[] args){ Set<String> set = new HashSet<String>(); String s1="hello"; String s2=s1; String s3=new String("world"); set.add(s1); set.add(s2); set.add(s3); System.out.print(s2.equals(s1)); System.out.print(set.size()); } }
把s2 的对象引用赋值给s1,则2个位同一个引用,值是相同的。Set采用equals()比较对象是否相等。而不是“==”。
HastSet类
我们先从HashSet源码看起:
下面是HashSet的定义:
1 public classHashSet<E>
2 extendsAbstractSet<E>
3 implementsSet<E>, Cloneable, java.io.Serializable
HashSet继承了AbstractSet,实现了Set接口。其实AbstractSet已经实现Set接口了。AbstractSet继承自AbstractCollection,而AbstractCollection实现了Collection接口的部分方法,而Set接口和Collection接口完全一致,所以AbstractSet只是实现了AbstractCollection没有实现的Set接口的方法和重写了部分AbstractCollection已经实现的方法。AbstractSet实现了equql()、hashCode()、removeAll()功能的重写。
再看HashSet
下面是HashSet定义的属性:
1 private transient HashMap<E,Object> map;
2 private static final Object PRESENT = new Object();
想一下HashMap有什么特点:基于哈希表,存储键值对,Key不能相同等等。Key不能相同!这个特点是不是和Set的元素不能相同和类似?如果将Set的元素当成Map的Key,是否就保证了元素的不重复?!答案是肯定的。但是Map存储键值对,Key有了,那么Value呢?这正是第二个属性PERSENT的意义。看到PERSENT属性时一个Object对象,且是static和final的,它的用途就是当做Value存进map中。
总结一下,HashSet的实现方式大致如下,通过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。
这样看来HashSet应该很简单,应该只是使用了HashMap的部分内容来实现。
看下add()方法源码 ,即是通过HashMap实现,
public
boolean add(E e) {
returnmap.put(e,PRESENT)==null;
}
addAll方式是在抽象类AbstractCollection中实现,HashSet通过继承拥有该方法,采用循环插入实现。
public boolean addAll(Collection<? extends E> c) { boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; } iterator() public Iterator<E> iterator() { returnmap.keySet().iterator(); }
很清楚了,返回的是HashMap中KeySet的迭代器。
size()
public int size() {
return
map.size();
}
size()方法同样返回的是map的大小,所以HashSet根本就没定义size属性。
public boolean isEmpty() {
return map.isEmpty();
}
既然size()用的是map的大小,那么isEmpty()自然也是判断map。
public boolean contains(Object o) { return map.containsKey(o); } public voidclear() { map.clear(); } public Objectclone() { try { HashSet<E> newSet =(HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } }
这几个方法就不解释了。
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
remove(Object o)为什么还要判断结果呢?因为通过HashSet存入的元素,所对应的Value值都是PERSENT,如果传入的o不存在,map的remove方法返回为null,则对应的结果是HashSet的remove操作应该放回false,所以这里根据返回的结果判断是否移除成功。
在Object类中定义了hashCode()和equals()方法,equals()方法按照内存地址比较对象是否相等,因此如果object1.equals(object2)为true,则表明两个变量引用同一个对象,其哈希码也肯定相等。如果用户定义一个类覆盖了Object类的equals()方法,但没有覆盖Object类的hashCode()方法,就会会导致,当object1.equals(object2)为true,而哈希码不一定一样,导致HashSet或HashMap无法正常工作。
package com.bin.proxy;
class Customer {
String name;
int
age;
public String getName() {
return
name;
}
public
void setName(String name) {
this.name = name;
}
public
int getAge() {
return
age;
}
public
void setAge(int age) {
this.age = age;
}
public
boolean equals(Object o){
if(this ==o)returntrue;
if((o
instanceof Test5))returnfalse;
final Customer test=(Customer)o;
if(this.name.equals(test.getName())&&this.age==test.getAge())
return
true;
else
return
false;
}
public
int hashCode(){
int result;
result=(name ==null?0:name.hashCode());
result = 29*result+age;
return result;
}
}
public
class Test5{
public
static void main(String[] args){
Customer t1= new Customer();
Customer t2= new Customer();
t1.setAge(4);
t1.setName("song");
t2.setAge(4);
t2.setName("song");
System.out.print(t1.equals(t2));
}
}
如上程序,若把hashCode()去掉,则在HashSet和HashMap代表不同的对象。
四、TreeSet类
其构造方法为:
构造方法摘要 | |
TreeSet() 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。 | |
TreeSet(Collection<? extendsE> c) 构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。 | |
TreeSet(Comparator<? superE> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。 | |
TreeSet(SortedSet<E> s) 构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。 |
Set<Integer>set =new TreeSet<Integer>();
set.add(newInteger(8));
set.add(new Integer(7));
set.add(new Integer(9));
则程序打印set时显示 6 7 8 9。
TreeSet支持两种排序方式:自然排序和客户化排序,默认采用自然排序
1、 自然排序
在JDK中有个Comparable接口,部分类实现了他,如Integer,Double和String等,其有个接口compareTo(Object o)方法,x.compareTo(y),返回0,表示相等,若大于0表示x大于y,反之小于0,表示x小于y。
TreeSet调用compareTo()方法比较集合对象的大小,然后进行升序排序,成为自然排序,使用自然排序的时候,只能想TreeSetJ集合加入同类型的对象,这些对象必须实现了Comparable接口,
2、 客户化排序
除了自然排序,TreeSet还支持客户化排序,java.util.Comparator<Type>接口提供具体排序方式,Comparator有个compare(Type x,Type y)方法,用于比较对象大小,当返回大于0时,表示x大于y,当放回小于0时,表示x小于y,当返回等于0时,表示x等于y
package com.bin.jihe; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class CustomerComparator implements Comparator<Customer>{ public static void main(String[] args) { Set<Customer> set=new TreeSet<Customer>(newCustomerComparator()); set.add(new Customer(11,"tom")); set.add(new Customer(12,"jim")); set.add(new Customer(10,"ljcy")); Iterator <Customer>it=set.iterator(); while(it.hasNext()){ Customer customer=it.next(); System.out.println(customer.getAge()+"-"+customer.getName()); } } @Override public int compare(Customer o1, Customer o2) { if(o1.getName().compareTo(o2.getName())>0)return 1; if(o1.getName().compareTo(o2.getName())<0)return -1; return 0; } }
相关文章推荐
- 【JAVA基础】集合类源码分析_HashMap/HashSet
- 【Java】 集合类概述-List、Map、Set
- Java中的Map List Set等集合类
- java中三大集合类Map,Set,List的详细介绍
- Java基础14:集合类;list集合;迭代器;set集合;
- Java基本概念:集合类 List/Set/Map... 的区别和联系
- Java基本概念:集合类 List/Set/Map... 的区别和联系
- 【转】Java基本概念:集合类 List/Set/Map...的区别
- Java中的Map List Set等集合类
- Java中的Map List Set等集合类
- JAVA中的Map List Set等集合类
- 【转】Java基本概念:集合类 List/Set/Map...的区别
- Java 深入学习(1) —— 容器类(集合类)Set、List、Queue、Map 之间的区别
- Java中的Map List Set等集合类
- Java中的Set集合类
- Java中的Map List Set等集合类
- Java基本概念:集合类 List/Set/Map 的区别和联系
- Java笔记——集合类:set用法
- java 集合类 map/set/list/vector之间的关系
- Java中的Map List Set等集合类