Set接口也是Collection接口下的一个子接口,它主要包含以下三个实现类:
1:HashSet
2:LinkedHashSet
3:Treeset
其中,HashSet是Set接口中的主要实现类,下面具体介绍一下Set
1:Set类和List类相反,它所存入的元素的顺序是无序性的并且是不可重复的,也就是说事先存入的时候我们并不能够知道被存入元素将会被放在哪个位置,并且如果尝试存入多个相同的元素,只有第一个能被存进去。
Set类中的方法是和其父类Collection使用的方法是相同的,具体使用方法请看我的另一篇文章:容器 ;
下面来看Set主要实现类Hasnset的具体例子
import
java.util.HashSet;
import
java.util.Set;
import
org.junit.Test;
public
class
TestSet {
@Test
public
void
TestHashSet1 (){
Woman
P1
=
new
Woman(
"美女"
,20);
Woman
P2
=
new
Woman(
"美女"
,20);
Set
A
=
new
HashSet();
A
.add(123);
A
.add(
"帅哥"
);
A
.add(
"小孩"
);
A
.add(
"小孩"
);
A
.add(
P1
);
A
.add(
P2
);
System.
out
.println(
A
.size());
System.
out
.println(
A
);
}
}
class
Woman{
private
String
name
;
private
int
age
;
public
Woman() {
super
();
}
public
Woman(String
name
,
int
age
) {
super
();
this
.
name
=
name
;
this
.
age
=
age
;
}
@Override
public
String toString() {
return
"Woman [name="
+
name
+
", age="
+
age
+
"]"
;
}
}//输出结果如下
//
5
//[小孩, 123, 帅哥, Woman [name=美女, age=20], Woman [name=美女, age=20]]
通过上面的例子,我们可以看到存入其中的对象的顺序确实是无序的,但是我们发现了一个问题,就是在存入一个对象Woman时,存入两个相同的对象P1与P2居然存了进去,而存入两个 "小孩" 确没有存进去,这是有原因的:
2:由于Set中的元素存入时会使用哈希算法从而达到无序存入元素的目的,当我们在Set中添加对象时,会先调用此对象所在类的hashCode()方法,产生一个哈希值,这个哈希值决定了此对象在Set中被存入的位置,如果在此位置上没有对象存储,则存入这个对象,若已有对象存入,则继续调用equals()方法去比较两个对象是否完全相同,如果相同,则后一个对象时不能够被添加进来的,那么也有以下这几种情况:
①两个对象的哈希值相同,但是所含的内容不同
②两个对象所含的内容相同,但是算出来的哈希值却是不同的
出现上面情况的原因主要是hashCode()方法中的哈希算法出了问题,也就是说你的哈希算法不够严谨,通常我们
要求在添加进Set中的元素所在的类是必须要重写equals()方法和hashCode()方法的,就像上面那个例子一样,由于Woman类中没有重写这两个方法,所以它调用的equals()和hashCode()方法都是Object类中的(会比较他们的地址想不相同,显然P1与P2的地址是不同的),自然就导致相同的对象都被存了进去,具体的重写方法如下:
@Override
public
int
hashCode() {
final
int
prime
= 31;
int
result
= 1;
result
=
prime
*
result
+
age
;
result
=
prime
*
result
+ ((
name
==
null
) ? 0 :
name
.hashCode());
return
result
;
}
@Override
public
boolean
equals(Object
obj
) {
if
(
this
==
obj
)
return
true
;
if
(
obj
==
null
)
return
false
;
if
(getClass() !=
obj
.getClass())
return
false
;
Woman
other
= (Woman)
obj
;
if
(
age
!=
other
.
age
)
return
false
;
if
(
name
==
null
) {
if
(
other
.
name
!=
null
)
return
false
;
}
else
if
(!
name
.equals(
other
.
name
))
return
false
;
return
true
;
}
所以在今后的开发过程中,在使用到Set类的时候,一定要重写将存入其中的对象所属类中的equals()方法和hashCode()方法。
********************************************************************************************************************************************************
下面来看一个有意思的事情,利用LinkedHashSet实现类去实现Set:
import
java.util.HashSet;
import
java.util.Iterator;
import
java.util.LinkedHashSet;
import
java.util.Set;
import
org.junit.Test;
@
Test
public
void
TestLinkedHashSet1 (){
Set
A
=
new
LinkedHashSet();
A
.add(123);
A
.add(
"帅哥"
);
A
.add(
"小孩"
);
A
.add(
"MM"
);
Iterator
iterator
=
A
.iterator();
while
(
iterator
.hasNext())
{
System.
out
.println(
iterator
.next());
}
}
输出结果如下:
123
帅哥
小孩
MM
细心的朋友可能发现了,这个通过迭代器遍历LinkedHashSet类输出的顺序怎么是按照添加的顺序来的呢,Set类不是说是无序的吗?这里就要说说它的另一个实现类LinkedHashSet了,他使用链表纪录了添加进集合中的顺序,导致我们遍历LinkedHashSet集合的元素时是按照添加的顺序输出的,请看图:
从上图我们可以看到当元素计算完哈希值存入时,指针会按顺序指向下一个插入的结点从而导致了遍历时是按照插入顺序遍历的
所以,从上面的例子可以显然看出,LiknedHashSet的插入速度要比HashSet慢,但是在迭代访问Set集合中的所有元素时,LinkedHashSet的速度是要优于HashSet的