HashSet
HashSet 是按照Hash算法来存储集合里的元素,具有良好的存储和查找功能。
HashSet有以下特点:
- 不能保证元素的排列顺序,顺序有可能发生变化。
- 集合元素值是可以为NULL的。
- HashSet不是同步的,如果多个线程同时访问HashSet集合,且同时有多条线程修改HashSet集合时,必须通过代码保证同步
HashCode值有点类似于数组的索引值
HashSet集合判断两个元素相等的标准是两个对象equals方法返回true,并且两个对象的hashCode()方法返回值也相等
class A {
public boolean equals(Object obj)
{
return true;
}
}
class B{
public int hashCode(){
return 1;
}
}
class C{
public boolean equals(Object obj)
{
return true;
}
public int hashCode(){
return 2;
}
}
public class T4 {
public static void main(String[] args) {
HashSet books = new HashSet();
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
}
}
结果为:[Test.B@1, Test.B@1, Test.C@2, Test.A@74a14482, Test.A@4554617c] C类只有一个
LinkedHashSet
也是根据元素hashCode值来决定元素的位置,同时使用链表维护元素的次序,遍历时会按照添加顺序来。
public static void main(String[] args) {
LinkedHashSet c = new LinkedHashSet();
c.add("我爱你");
c.add("奇松巴");
c.add("JJ");
c.remove("奇松巴");
c.add("奇松巴");
System.out.println(c);
}
//输出结果:[我爱你, JJ, 奇松巴] 先进排前面
TreeSet
是SortedSet接口的唯一实现,可确保集合的元素处于排序状态,在前面的HashSet集合相比,TreeSet有额外的方法提供:
- Comparator comparator():返回当前Set使用的Comparator,或者返回null,表示自然排序
- Object first():返回集合中的第一个元素。
- Object first():返回集合中的最后一个元素。
- Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet的元素)
- Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet的元素)
- SortedSet subSet(fromElement,toElement):返回此Set的子集合,范围从fromElemnt(包含)到toElement(不包含)
- SortedSet headSet(toElement):返回Set的子集,由小于toElement的元素组成
- SortedSet tailSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成.
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
System.out.println(nums);
// 结果[-9, 2, 5, 10]
System.out.println(nums.first());
System.out.println(nums.last());
System.out.println(nums.headSet(4));
System.out.println(nums.tailSet(5));
System.out.println(nums.subSet(-3,4));
//-9
//10
//[-9, 2]
//[5, 10]
//[2]
}
TreeSet采用的是红黑树的数据结构对元素进行排序,支持两种排序方式,默认情况下是自然排序。
自然排序
Tree会调用集合元素的compareTo(Object obj)的方法来比较元素之间的大小关系,然后将元素按升序排序,这就是自然排序
public class T7 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Err());
ts.add(new Err());
}
}
Exception in thread “main” java.lang.ClassCastException: Test.Err cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at Test.T7.main(T7.java:9)
向TreeSet集合中添加元素时,只有第一个元素可以无须实现Comparable接口,后面添加的所有元素都必须实现Comparable接口,当然这也不是一种好做法,当试图从TreeSet中取出元素时,依然会引发异常
注意:大部分类在实现compareTo方法时,都需将被比较的类型转换为相同类型,因为同类才能比较大小,所以集合中其他元素与该元素应是同一类的实例。否在会引发异常。
定制排序
由自己来定制排序的规律逻辑,例如降序排序,实现定制排序需要在创建TreeSet集合对象时提供一个Comparator对象与该TreeSet集合关联,由Compatator对象负责集合元素的排序逻辑。
class M{
int age;
public M(int age)
{
this.age = age;
}
public String toString(){
return "M对象(age:"+age+")";
}
}
public class A1 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
M m1 = (M) o1;
M m2 = (M) o2;
if (m1.age>m2.age)
{
return -1;
}
else if (m1.age == m2.age)
{
return 0;
}
else
{
return 1;
}
}
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}
[M对象(age:9), M对象(age:5), M对象(age:-3)]
EnumSet类
专门为枚举类设计的集合类,EnumSet所有值必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序决定集合元素的顺序。
- EnumSet是以位向量的形式存储,占用内存很小运行效率高。
- EnumSet无法加入null元素
总结
HashSet和TreeSet的选择
- HashSet的性能相对好,优先
- 当你需要一个保存排序的Set时,应该使用TreeSet。
- LinkedHashSet由遍历优势,对于增删改查对于HashSet较慢。