不少大公司的面试题中会问TreeSet和HashSet有什么区别。此外linkedHashSet也是Set的一种实现类,下面归纳的是三者的特点。
HashSet是基于哈希表(Hash表)实现的,它不能保证线程安全,其中允许存在null元素,但null元素只能有1个。
当程序员向HashSet中插入一个对象时, HashSet会调用该对象的hashCode()方法(如果该对象没定义,会调用Object)来得到该对象的hashCode值;然后会根据hashCode值来决定该对象在HashSet中的存放位置,如果遇到两个对象的hashCode值一致的情况则说明它们相等, HashSet同样不会允许插入重复的元素。
上述描述包含了一层隐藏的含义, HashSet不能保证插入次序和遍历次序一致。
相比之下,linkedHashSet同样是基于Hash表,它也是根据元素的hashCode值来决定元素的存储位置的,但是它同时也采用了链表的方式来保证插入次序和遍历次序一致。下面可linketHashSetDemo.java例子看出两者的区别。
import java.util.HashSet; import java.util.Iterator; import java.util.linkedHashSet; import java.util.Set; public class linkedHashSetDemo { public static void main(String[] args) { SetstrHashSet = new HashSet (); Set strlinkedHashSet = new linkedHashSet (); for(int i = 0; i<10; i++){ strHashSet.add(String.valueOf(i)); strlinkedHashSet.add(String.valueOf(i)); } Iterator setStringIt = strHashSet.iterator(); while (setStringIt.hasNext()) { System.out.print(setStringIt.next()+" "); } System.out.println(); Iterator linkedSetStringIt = strlinkedHashSet.iterator(); while (linkedSetStringIt.hasNext()) { System.out.print(linkedSetStringIt.next()+" "); } } }
在main函数的第11- 14行中,通过for循环语句向两种集合中依次放入了1-10
这10个String类型的对象,然后通过迭代器,在第17~21行中通过两个while循环分别按顺序输出它们的值,结果如下。
1 3 2 1 0 7 6 5 4 9 8 2 0 1 2 3 4 5 6 7 8 9
第1行是针对HashSet的输出,从中可以看到它的遍历结果和插入的次序不一致;而,第2行是针对linkedHashSet,输出次序和插入次序一致。
而TreeSet是SortedSet接口的唯一实现类,它是用二叉树存储数据的方式来保证存储的元素处于有序状态,下面来看TreeSetDemo.java例子。
import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class TreeSetDemo { public static void main(String[] args){ SettreeSet = new TreeSet (); treeSet.add("4"); treeSet.add("3"); treeSet.add("1"); treeSet.add("2"); Iterator setStringIt = treeSet.iterator(); while(setStringIt.hasNext()){ System.out.print(setStringIt.next() + " "); } } }
在第4行中,以泛型的方式创建了一个TreeSet,并从第6-9行,以无序的方式插入了4个String对象。注意, TreeSet不允许插入null值,所以运行第10行的代码时会有异常。
当程序员在第14行通过迭代器遍历TreeSet对象时,会发现输出的次序和插入次序不一致,而且数据已经被排序,结果如下。
1 2 3 4
如果TreeSet中存储的不是上例中的基本数据类型,而是自定义的class,那么这个类必须实现Comparable接口中的compareTo方法, TreeSet会根据compareTo中的定义来区分大小,最终确定TreeSet中的次序。
程序员可以在compareTo方法中定义排序依据。在前面的compareTo方法中,是以学生的id作为判断依据的,如果两个学生的id相等,那么这个方法的返回值是0,说明这两个学生是相等的(是同一个学生)。
此外,该方法还可以返回1或-1,其中1表示大于,-1表示小于。下面通过SortedStudent.java例子来看TreeSet是如何对自定义类进行排序的。
import java.util.Iterator; import java.util.Set; import java.util.TreeSet; class SortedStudent { private int id; public SortedStudent(int id) {this.id = id;} public int getId(){return id;} public boolean equals(SortedStudent stu) { if(stu.getId() == this.id) { return true; } else { return false; } } public int compareTO(Object obj){ if(obj instanceof SortedStudent) { SortedStudent s = (SortedStudent) obj; if(s.getId() == this.getId()) { return 0; } else{ return s.getId()>this.getId()?-1:1; } }else{ return 0; } } } public class SortStudentByID{ public static void main(String[] args){ SortedStudent s1 = new SortedStudent(1); SortedStudent s2 = new SortedStudent(2); SortedStudent s3 = new SortedStudent(3); SortedStudent s4 = new SortedStudent(4); SetstuSet = new TreeSet (); stuSet.add(s2); stuSet.add(s4); stuSet.add(s1); stuSet.add(s3); Iterator itStu = stuSet.iterator(); while(itStu.hasNext()){ System.out.println(itStu.next().getId()); } } }
在第2行定义的SortedStudent类的代码中,实现了Compareable接口。在第16行,程序员重写了compareTo方法,在这个方法中,如果两个学生对象的id相等,则认为它们相等,否则将用id的大小来判断大小。
在第38-41行中,虽然程序员用乱序的方式放入了4个学生对象,但TreeSet会自动地对它们进行排序,这可以从第46行的输出结果中得到验证。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)