JAVA基础知识之Set集合



LinkedHashSet的特征

LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。查看LinkedHashSet的源码发现它是样的,

[java]  view plain  copy
  1. //LinkedHashSet 源码  
  2. public class LinkedHashSet extends HashSet  
  3.     implements Set, Cloneable, Serializable  
  4. {  
  5.   
  6.     public LinkedHashSet(int i, float f)  
  7.     {  
  8.         super(i, f, true);  
  9.     }  
  10.   
  11. ....  

在JAVA7中, LinkedHashSet没有定义任何方法,只有四个构造函数,它的构造函数调用了父类(HashSet)的带三个参数的构造方法,父类的构造函数如下,

[java]  view plain  copy
  1. //HashSet构造函数  
  2.   
  3.     HashSet(int i, float f, boolean flag)  
  4.     {  
  5.         map = new LinkedHashMap(i, f);  
  6.     }  
  7.   
  8. ......  
由此可知,LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的所有方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap.

下面是一个LinkedHashSet维持元素插入顺序的例子,

[java]  view plain  copy
  1. package colection.HashSet;  
  2.   
  3. import java.util.LinkedHashSet;  
  4.   
  5. public class LinkedHashSets {  
  6.     public static void main(String[] args) {  
  7.         LinkedHashSet lhs = new LinkedHashSet();  
  8.         lhs.add("abc");  
  9.         lhs.add("efg");  
  10.         lhs.add("hij");  
  11.         System.out.println(lhs);  
  12.         lhs.remove(new String("efg"));  
  13.         lhs.add("efg");  
  14.         System.out.println(lhs);  
  15.     }  
  16. }  

输入如下

[java]  view plain  copy
  1. [abc, efg, hij]  
  2. [abc, hij, efg]  

TreeSet类的特征

TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().

TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。

自然排序(在元素中写排序规则)

TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口,下面举例说明没有实现Comparable存入TreeSet时引发异常的情况。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.TreeSet;  
  4.   
  5. class Err {  
  6.       
  7. }  
  8.   
  9. public class TreeSets {  
  10.   
  11.     public static void main(String[] args) {  
  12.         TreeSet ts =  new TreeSet();  
  13.         ts.add(new Err());  
  14.         ts.add(new Err());  
  15.         System.out.println(ts);  
  16.               
  17.     }  
  18. }  
运行程序会抛出如下异常

[java]  view plain  copy
  1. Exception in thread "main" java.lang.ClassCastException: collection.Set.Err cannot be cast to java.lang.Comparable  
  2.     at java.util.TreeMap.compare(Unknown Source)  
  3.     at java.util.TreeMap.put(Unknown Source)  
  4.     at java.util.TreeSet.add(Unknown Source)  
  5.     at collection.Set.TreeSets.main(TreeSets.java:13)  
将上面的Err类实现Comparable接口之后程序就能正常运行了

[java]  view plain  copy
  1. class Err implements Comparable {  
  2.     @Override  
  3.     public int compareTo(Object o) {  
  4.         // TODO Auto-generated method stub  
  5.         return 0;  
  6.     }  
  7. }  

还有个重要问题是,因为TreeSet会调用元素的compareTo方法,这就要求所有元素的类型都相同,否则也会发生异常。 也就是说,TreeSet只允许存入同一类的元素 。例如下面这个例子就会抛出类型转换异常

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.TreeSet;  
  4.   
  5. class Err implements Comparable {  
  6.     @Override  
  7.     public int compareTo(Object o) {  
  8.         // TODO Auto-generated method stub  
  9.         return 0;  
  10.     }  
  11. }  
  12.   
  13. public class TreeSets {  
  14.   
  15.     public static void main(String[] args) {  
  16.         TreeSet ts =  new TreeSet();  
  17.         ts.add(1);  
  18.         ts.add("2");  
  19.         System.out.println(ts);  
  20.               
  21.     }  
  22. }  
运行结果
[java]  view plain  copy
  1. Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String  
  2.     at java.lang.String.compareTo(Unknown Source)  
  3.     at java.util.TreeMap.put(Unknown Source)  
  4.     at java.util.TreeSet.add(Unknown Source)  
  5.     at collection.Set.TreeSets.main(TreeSets.java:18)  

定制排序(在集合中写排序规则)

TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个 Comparator对象,由Comparator提供排序逻辑。下面就是一个使用Lambda表达式代替Comparator对象来提供定制排序的例子。 下面是一个定制排序的列子

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.TreeSet;  
  5.   
  6. class M {  
  7.     int age;  
  8.     public M(int age) {  
  9.         this.age = age;  
  10.     }  
  11.       
  12.     public String toString() {  
  13.         return "M[age:" + age + "]";  
  14.     }  
  15.       
  16. }  
  17.   
  18. class MyCommpare implements Comparator{  
  19.       
  20.     public int compare(Object o1, Object o2){  
  21.         M m1 = (M)o1;  
  22.         M m2 = (M)o2;  
  23.         return m1.age >  m2.age ? 1 : m1.age < m2.age ? -1 : 0;         
  24.     }  
  25.   
  26. }  
  27.   
  28. public class TreeSets {  
  29.   
  30.     public static void main(String[] args) {  
  31.         TreeSet ts =  new TreeSet(new MyCommpare());      
  32.         ts.add(new M(5));  
  33.         ts.add(new M(3));  
  34.         ts.add(new M(9));  
  35.         System.out.println(ts);  
  36.               
  37.     }  
  38. }  

当然将Comparator直接写入TreeSet初始化中也可以。如下。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.TreeSet;  
  5.   
  6. class M {  
  7.     int age;  
  8.     public M(int age) {  
  9.         this.age = age;  
  10.     }  
  11.       
  12.     public String toString() {  
  13.         return "M[age:" + age + "]";  
  14.     }  
  15.       
  16. }  
  17.   
  18. public class TreeSets {  
  19.   
  20.     public static void main(String[] args) {  
  21.         TreeSet ts =  new TreeSet(new Comparator() {  
  22.             public int compare(Object o1, Object o2) {  
  23.                 M m1 = (M)o1;  
  24.                 M m2 = (M)o2;  
  25.                 return m1.age >  m2.age ? -1 : m1.age < m2.age ? 1 : 0;     
  26.             }  
  27.         });   
  28.         ts.add(new M(5));  
  29.         ts.add(new M(3));  
  30.         ts.add(new M(9));  
  31.         System.out.println(ts);  
  32.               
  33.     }  
  34. }  

EnumSet特征

EnumSet顾名思义就是专为枚举类型设计的集合,因此集合元素必须是枚举类型,否则会抛出异常。 EnumSet集合也是有序的,其顺序就是Enum类内元素定义的顺序。EnumSet存取的速度非常快,批量操作的速度也很快。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并没有提供任何构造函数,要创建一个EnumSet集合对象,只需要调用allOf等方法,下面是一个EnumSet的例子。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.EnumSet;  
  4.   
  5. enum Season  
  6. {  
  7.     SPRING, SUMMER, FALL, WINTER  
  8. }  
  9. public class EnumSets {  
  10.   
  11.     public static void main(String[] args) {  
  12.         //必须用元素对象的类类型来初始化,即Season.class  
  13.         EnumSet es1 = EnumSet.allOf(Season.class);  
  14.         System.out.println(es1);  
  15.         EnumSet es2 = EnumSet.noneOf(Season.class);  
  16.         es2.add(Season.WINTER);  
  17.         es2.add(Season.SUMMER);  
  18.         System.out.println(es2);  
  19.         EnumSet es3 = EnumSet.of(Season.WINTER, Season.SUMMER);  
  20.         System.out.println(es3);  
  21.         EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER);  
  22.         System.out.println(es4);  
  23.         EnumSet es5 = EnumSet.complementOf(es4);  
  24.         System.out.println(es5);  
  25.     }  
  26. }  
执行结果

[java]  view plain  copy
  1. [SPRING, SUMMER, FALL, WINTER]  
  2. [SUMMER, WINTER]  
  3. [SUMMER, WINTER]  
  4. [SUMMER, FALL, WINTER]  
  5. [SPRING]  

各种集合性能分析

  • HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。
  • LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。
  • EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素


LinkedHashSet的特征

LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。查看LinkedHashSet的源码发现它是样的,

[java]  view plain  copy
  1. //LinkedHashSet 源码  
  2. public class LinkedHashSet extends HashSet  
  3.     implements Set, Cloneable, Serializable  
  4. {  
  5.   
  6.     public LinkedHashSet(int i, float f)  
  7.     {  
  8.         super(i, f, true);  
  9.     }  
  10.   
  11. ....  

在JAVA7中, LinkedHashSet没有定义任何方法,只有四个构造函数,它的构造函数调用了父类(HashSet)的带三个参数的构造方法,父类的构造函数如下,

[java]  view plain  copy
  1. //HashSet构造函数  
  2.   
  3.     HashSet(int i, float f, boolean flag)  
  4.     {  
  5.         map = new LinkedHashMap(i, f);  
  6.     }  
  7.   
  8. ......  
由此可知,LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的所有方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap.

下面是一个LinkedHashSet维持元素插入顺序的例子,

[java]  view plain  copy
  1. package colection.HashSet;  
  2.   
  3. import java.util.LinkedHashSet;  
  4.   
  5. public class LinkedHashSets {  
  6.     public static void main(String[] args) {  
  7.         LinkedHashSet lhs = new LinkedHashSet();  
  8.         lhs.add("abc");  
  9.         lhs.add("efg");  
  10.         lhs.add("hij");  
  11.         System.out.println(lhs);  
  12.         lhs.remove(new String("efg"));  
  13.         lhs.add("efg");  
  14.         System.out.println(lhs);  
  15.     }  
  16. }  

输入如下

[java]  view plain  copy
  1. [abc, efg, hij]  
  2. [abc, hij, efg]  

TreeSet类的特征

TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().

TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。

自然排序(在元素中写排序规则)

TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口,下面举例说明没有实现Comparable存入TreeSet时引发异常的情况。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.TreeSet;  
  4.   
  5. class Err {  
  6.       
  7. }  
  8.   
  9. public class TreeSets {  
  10.   
  11.     public static void main(String[] args) {  
  12.         TreeSet ts =  new TreeSet();  
  13.         ts.add(new Err());  
  14.         ts.add(new Err());  
  15.         System.out.println(ts);  
  16.               
  17.     }  
  18. }  
运行程序会抛出如下异常

[java]  view plain  copy
  1. Exception in thread "main" java.lang.ClassCastException: collection.Set.Err cannot be cast to java.lang.Comparable  
  2.     at java.util.TreeMap.compare(Unknown Source)  
  3.     at java.util.TreeMap.put(Unknown Source)  
  4.     at java.util.TreeSet.add(Unknown Source)  
  5.     at collection.Set.TreeSets.main(TreeSets.java:13)  
将上面的Err类实现Comparable接口之后程序就能正常运行了

[java]  view plain  copy
  1. class Err implements Comparable {  
  2.     @Override  
  3.     public int compareTo(Object o) {  
  4.         // TODO Auto-generated method stub  
  5.         return 0;  
  6.     }  
  7. }  

还有个重要问题是,因为TreeSet会调用元素的compareTo方法,这就要求所有元素的类型都相同,否则也会发生异常。 也就是说,TreeSet只允许存入同一类的元素 。例如下面这个例子就会抛出类型转换异常

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.TreeSet;  
  4.   
  5. class Err implements Comparable {  
  6.     @Override  
  7.     public int compareTo(Object o) {  
  8.         // TODO Auto-generated method stub  
  9.         return 0;  
  10.     }  
  11. }  
  12.   
  13. public class TreeSets {  
  14.   
  15.     public static void main(String[] args) {  
  16.         TreeSet ts =  new TreeSet();  
  17.         ts.add(1);  
  18.         ts.add("2");  
  19.         System.out.println(ts);  
  20.               
  21.     }  
  22. }  
运行结果
[java]  view plain  copy
  1. Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String  
  2.     at java.lang.String.compareTo(Unknown Source)  
  3.     at java.util.TreeMap.put(Unknown Source)  
  4.     at java.util.TreeSet.add(Unknown Source)  
  5.     at collection.Set.TreeSets.main(TreeSets.java:18)  

定制排序(在集合中写排序规则)

TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个 Comparator对象,由Comparator提供排序逻辑。下面就是一个使用Lambda表达式代替Comparator对象来提供定制排序的例子。 下面是一个定制排序的列子

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.TreeSet;  
  5.   
  6. class M {  
  7.     int age;  
  8.     public M(int age) {  
  9.         this.age = age;  
  10.     }  
  11.       
  12.     public String toString() {  
  13.         return "M[age:" + age + "]";  
  14.     }  
  15.       
  16. }  
  17.   
  18. class MyCommpare implements Comparator{  
  19.       
  20.     public int compare(Object o1, Object o2){  
  21.         M m1 = (M)o1;  
  22.         M m2 = (M)o2;  
  23.         return m1.age >  m2.age ? 1 : m1.age < m2.age ? -1 : 0;         
  24.     }  
  25.   
  26. }  
  27.   
  28. public class TreeSets {  
  29.   
  30.     public static void main(String[] args) {  
  31.         TreeSet ts =  new TreeSet(new MyCommpare());      
  32.         ts.add(new M(5));  
  33.         ts.add(new M(3));  
  34.         ts.add(new M(9));  
  35.         System.out.println(ts);  
  36.               
  37.     }  
  38. }  

当然将Comparator直接写入TreeSet初始化中也可以。如下。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.Comparator;  
  4. import java.util.TreeSet;  
  5.   
  6. class M {  
  7.     int age;  
  8.     public M(int age) {  
  9.         this.age = age;  
  10.     }  
  11.       
  12.     public String toString() {  
  13.         return "M[age:" + age + "]";  
  14.     }  
  15.       
  16. }  
  17.   
  18. public class TreeSets {  
  19.   
  20.     public static void main(String[] args) {  
  21.         TreeSet ts =  new TreeSet(new Comparator() {  
  22.             public int compare(Object o1, Object o2) {  
  23.                 M m1 = (M)o1;  
  24.                 M m2 = (M)o2;  
  25.                 return m1.age >  m2.age ? -1 : m1.age < m2.age ? 1 : 0;     
  26.             }  
  27.         });   
  28.         ts.add(new M(5));  
  29.         ts.add(new M(3));  
  30.         ts.add(new M(9));  
  31.         System.out.println(ts);  
  32.               
  33.     }  
  34. }  

EnumSet特征

EnumSet顾名思义就是专为枚举类型设计的集合,因此集合元素必须是枚举类型,否则会抛出异常。 EnumSet集合也是有序的,其顺序就是Enum类内元素定义的顺序。EnumSet存取的速度非常快,批量操作的速度也很快。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并没有提供任何构造函数,要创建一个EnumSet集合对象,只需要调用allOf等方法,下面是一个EnumSet的例子。

[java]  view plain  copy
  1. package collection.Set;  
  2.   
  3. import java.util.EnumSet;  
  4.   
  5. enum Season  
  6. {  
  7.     SPRING, SUMMER, FALL, WINTER  
  8. }  
  9. public class EnumSets {  
  10.   
  11.     public static void main(String[] args) {  
  12.         //必须用元素对象的类类型来初始化,即Season.class  
  13.         EnumSet es1 = EnumSet.allOf(Season.class);  
  14.         System.out.println(es1);  
  15.         EnumSet es2 = EnumSet.noneOf(Season.class);  
  16.         es2.add(Season.WINTER);  
  17.         es2.add(Season.SUMMER);  
  18.         System.out.println(es2);  
  19.         EnumSet es3 = EnumSet.of(Season.WINTER, Season.SUMMER);  
  20.         System.out.println(es3);  
  21.         EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER);  
  22.         System.out.println(es4);  
  23.         EnumSet es5 = EnumSet.complementOf(es4);  
  24.         System.out.println(es5);  
  25.     }  
  26. }  
执行结果

[java]  view plain  copy
  1. [SPRING, SUMMER, FALL, WINTER]  
  2. [SUMMER, WINTER]  
  3. [SUMMER, WINTER]  
  4. [SUMMER, FALL, WINTER]  
  5. [SPRING]  

各种集合性能分析

  • HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。
  • LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。
  • EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值