一、Set接口
Set是一种不包含重复的元素的Collection,Set最多有一个null元素。很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
继承结构:
publicinterface Set<E> extends Collection<E>{}
publicabstract class AbstractSet<E> extends AbstractCollection<E>implements Set<E>{}
public classCopyOnWriteArraySet<E>extends AbstractSet<E>implementsSerializable{}
public abstract class EnumSet<E extendsEnum<E>>extends AbstractSet<E>implements Cloneable,Serializable{}
public class HashSet<E>extendsAbstractSet<E>implements Set<E>, Cloneable,Serializable{}
public final class JobStateReasonsextendsHashSet<JobStateReason>implements PrintJobAttribute{}
publicclass LinkedHashSet<E>extends HashSet<E>implementsSet<E>, Cloneable, Serializable{}
public classTreeSet<E>extends AbstractSet<E>implements SortedSet<E>,Cloneable,Serializable{}
可以看出,可以实例化的类为: HashSet , LinkedHashSet , TreeSet , CopyOnWriteArraySet 。
java中向set中添加对象,如何确保不相同,需要看hashcode(),equals(),compareTo()方法的作用。
二:HashSet
1)Hashset数据结构:
publicclass HashSet<E> extends AbstractSet<E> implementsSet<E>, Cloneable, java.io.Serializable{
static finallong serialVersionUID = -5024744406713321676L;
privatetransient HashMap<E,Object> map;
//这是每个键所指的对像
privatestatic final Object PRESENT = new Object();
publicHashSet() {
map = new HashMap<E,Object>();
}
publicboolean add(E o) {
return map.put(o,PRESENT)==null;
}
//以下省略..........
}
可以看到 HashSet 使用了 HashMap 作为其 Map 保存“键-值”对。 Map 中的元素是“键-值”对,其中“键”必须是唯一的 ,HashSet 就是利用这个特性实现没有重复元素的。它把 set 中的元素作为 Map 中的“键”,从而保持元素的唯一性。
2) HashSet 应用实践——重写hashCode,equeals
publicclasstest{
publicstaticvoidmain(String[] args) {
Setset = newHashSet();
set.add(newSetElement5("aa"));
set.add(newSetElement5("aa"));
set.add(newSetElement5("bb"));
System.out.println(set);
}
staticclassSetElement5 {
Strings;
publicSetElement5(String s) {
this.s= s;
}
publicString toString() {
returns;
}
publicbooleanequals(Object obj) {
returns.equals(((SetElement5)obj).s);
}
publicinthashCode() {
//return super.hashCode();
returns.hashCode();
}
}
运行结果:[aa,bb]
SetElement5重写了hashCode方法和euqals方法。HashSet中是采用了先比较元素hashCode如果相同则比较equals的方法来判断元素是否相同(duplicate),如果不重写,则不同的对象(前两个对象)即可视为不同
3)具体应用:
我们可以重写hashCode,equals方法,如联系人类Contact.java 每个联系人都有一个唯一ID,相同ID的不同contact对象我们视为相同,不能添加到set中,
这样new了2个contact,如果id相同,则视为相同对象
publicinthashCode()
{
returngetId().hashCode();
}
publicbooleanequals(Object c)
{
if(c == this)
{
returntrue;
}
if(c instanceofContact)
{
if(getId() != null&& getId().equals(((Contact) c).getId()))
{
returntrue;
}
}
returnfalse;
}
请注意:必须小心操作可变对象。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
4)HashSet相关算法:从数组a中剔除数组a和数组b中含有的共同元素
publicclass Test1 {
public static void main(String[] args){
String a[] = {"a","a","b","c","d","d"};
String b[] = {"a","c","d","d","e","e"};
HashSet mHashSet = new HashSet();
for(int i = 0; i < b.length; i++){
if(!mHashSet.contains(b[i]));
mHashSet.add(b[i]);
}
for(int j = 0; j < a.length; j++){
if(!mHashSet.contains(a[j]))
System.out.println(a[j]);
}
}
}
二、 TreeSet
1)TreeSet 数据结构
TreeSet的部分实体:
publicclass TreeSet<E> extends AbstractSet<E> implementsSortedSet<E>, Cloneable, java.io.Serializable
{
privatetransient SortedMap<E,Object> m;
private transientSet<E> keySet;
//这是每个键所指的对像
privatestatic final Object PRESENT = new Object();
privateTreeSet(SortedMap<E,Object> m) {
this.m = m;
keySet= m.keySet();
}
public TreeSet() {
this(newTreeMap<E,Object>());
}
//以下省略..........
}
可以看到 TreeSet 使用了 SortedMap 作为其 Map 保存“键-值”对。 TreeSet 采用红黑二叉树的数据结构,添加到 TreeSet 中的数据会自动按升序排列。
2)TreeSet应用实践——实现Comparable接口
要用TreeSet来做为你的Set,那么Set中所装的元素都必须实现了Comparable接口。
publicclasstest7 {
publicstaticvoidmain(String[] args) {
Setset = newTreeSet();
set.add(newSetElement2("aa"));
set.add(newSetElement2("aa"));
set.add(newSetElement2("bb"));
System.out.println(set);
}
staticclassSetElement2 implementsComparable{
Strings;
publicSetElement2(String s) {
this.s= s;
}
publicString toString() {
returns;
}
publicintcompareTo(Objecto) {
returns.compareTo(((SetElement2)o).s);
}
publicbooleanequals(Object obj) {
returns.equals(((SetElement2)obj).s);
}
}
}
运行结果:[aa,bb]
要用TreeSet来做为你的Set,那么Set中所装的元素都必须实现了Comparable接口。TreeSet中是采用Comparable接口中的compareTo方法来判断元素是否相同(duplicate),而不是采用其他类似equals之类的东东来判断。
如果不实现该接口,程序能够正常编译,但是运行时会抛出异常java.lang.ClassCastException。
因为在TreeSet的add方法中需要比较两个元素的“值”。请看TreeMap中的compare方法:
privateint compare(K k1, K k2) {
return (comparator==null ?((Comparable</*-*/K>)k1).compareTo(k2):comparator.compare((K)k1, (K)k2));
}
可见这个方法先把要比较的元素downcast成Comparable类型。这里就可以解释“示例程序1”中为什么会抛出异常java.lang.ClassCastException,因SetElement1没有实现Comparable接口,当然就不能downcast成Comparable。
3)如果重写compareTo,返回-1,如下:
public int compareTo(Object o){
//returns.compareTo(((SetElement3)o).s);
return -1;
}
运行结果:
[bb,aa, aa]
这是因为compareTo返回值始终是"-1",也就是说“把任何元素都看成不同”。
4)排序,TreeSet中的数据会自动按升序排列,重写sort后,如下,按降序排列
public class test6 {
publicstaticvoidmain(String[] args) {
//Create a tree set
TreeSet<String>ts = newTreeSet<String>(newsort());
//Add elements to the tree set
ts.add("C");
ts.add("A");
ts.add("B");
ts.add("E");
ts.add("F");
ts.add("D");
//Get an iterator
Iterator<String>i = ts.iterator();
//Display elements
while(i.hasNext()) {
Objectelement = i.next();
System.out.print(element+ ",");
}
System.out.println();
}
}
classsort implementsComparator{
publicintcompare(Object a, Object b) {
StringaStr, bStr;
aStr= (String) a;
bStr= (String) b;
//重写compare方法
returnbStr.compareTo(aStr);
}
}
输出结果:F,E,D,C,B,A,
2.3 、 CopyOnWriteArraySet 部分:
类CopyOnWriteArraySet是java.util.concurrent包中的一个类,它是线程安全的。
CopyOnWriteArraySet是使用CopyOnWriteArrayList作为其盛放元素的容器。当往CopyOnWriteArrayList添加新元素,它都要遍历整个List,并且用equals来比较两个元素是否相同。
public class test7 {
publicstaticvoidmain(String[] args) {
Setset = newCopyOnWriteArraySet();
set.add(newSetElement6("aa"));
set.add(newSetElement6("aa"));
set.add(newSetElement6("bb"));
System.out.println(set);
}
staticclassSetElement6 {
Strings;
publicSetElement6(String s) {
this.s= s;
}
publicString toString() {
returns;
}
publicbooleanequals(Object obj) {
returns.equals(((SetElement6)obj).s);
}
}
}
运行结果:[aa,bb]