Java 7之集合类型第6篇 - Set集合的实现

来源:互联网 发布:麦克雷 数据 编辑:程序博客网 时间:2024/05/20 09:06

转载地址:http://blog.csdn.net/mazhimazh/article/details/19030037

Set接口中定义了一些Set常见的操作,与Collection接口中定义的方法差不多。AbstractSet抽象类中只实现了equals()、hashCode()和removeAll()方法,非常简单,有兴趣的读者可以自己去查看。


1、HashSet


HashSet类的特点能够快速定位集合中的元素、集合中的元素无序(这里所谓的无序并不是完全无序,只是不像List集合按对象的插入顺序保存对象) 
有了HashMap的实现,则HashSet的实现非常简单,只需要将Set中的元素做为Map中的key值来保存即可。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class HashSet<E>   extends AbstractSet<E>   implements Set<E>, Cloneable, java.io.Serializable{  
  2.     private transient HashMap<E,Object>  map; // 使用map的key来保存Set元素  
  3.     private static final Object PRESENT = new Object();// 值为一个Object对象  
  4.          
  5.     public HashSet() {  
  6.         map = new HashMap<>();  
  7.     }  
  8.   
  9.     /** 
  10.      * Constructs a new set containing the elements in the specified 
  11.      * collection.  The HashMap is created with default load factor 
  12.      * (0.75) and an initial capacity sufficient to contain the elements in 
  13.      * the specified collection. 
  14.      */  
  15.     public HashSet(Collection<? extends E> c) {  
  16.         map = new HashMap<>(Math.max((int) (c.size()/.75f) + 116));  
  17.         addAll(c);  
  18.     }  
  19.     // 省略其他构造函数  
  20. }  
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public Iterator<E> iterator() {     // 循环HashMap中的key值  
  2.     return map.keySet().iterator();  
  3. }  
  4. public boolean contains(Object o) { // 查找HashMap中的key值  
  5.     return map.containsKey(o);  
  6. }  
  7. public boolean add(E e) {           // key为Set中要添加的元素,值为一个空的Object对象  
  8.     return map.put(e, PRESENT)==null;  
  9. }  

由HashSet类实现的Set集合中的对象必须是惟一的,因此需要添加到由HashSet类实现的Set集合中的对象,需要重新实现equals()方法,从而保证插入集合中对象的标识的惟一性。 

由HashSet类实现的Set集合的排列方式为按照哈希码排序,根据对象的哈希码确定对象的存储位置,因此需要添加到由HashSet类实现的Set集合中的对象,还需要重新实现hashCode()方法,从而保证插入集合中的对象能够合理地分布在集合中,以便于快速定位集合中的对象。 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class Person{  
  2.     private String name;  
  3.     private long id_card;  
  4.     public Person(String name,long id_card){  
  5.         this.name = name;  
  6.         this.id_card = id_card;  
  7.     }  
  8.     public long getId_card(){  
  9.         return id_card;  
  10.     }  
  11.     public void setId_card(long id_card){  
  12.         this.id_card = id_card;  
  13.     }  
  14.     public String getName(){  
  15.         return name;  
  16.     }  
  17.     public void setName(String name){  
  18.         this.name = name;  
  19.     }  
  20.     public int hashCode(){//重新实现hashCode()方法  
  21.         final int PRIME = 31;  
  22.         int result = 1;  
  23.         result = PRIME*result+(int)(id_card^(id_card>>>32));  
  24.         result = PRIME*result+((name ==null)?0:name.hashCode());  
  25.         return result;  
  26.     }  
  27.     public boolean equals(Object obj){//重新实现equals()方法  
  28.         if(this == obj){  
  29.             return true;  
  30.         }  
  31.         if(obj == null){  
  32.             return false;  
  33.         }  
  34.         if(getClass()!=obj.getClass()){  
  35.             return false;  
  36.         }  
  37.         final Person other = (Person)obj;  
  38.         if(id_card!=other.id_card){  
  39.             return false;  
  40.         }  
  41.         if(name == null){  
  42.             if(other.name != null){  
  43.                 return false;  
  44.             }  
  45.         }  
  46.         else if(!name.equals(other.name)){  
  47.             return false;  
  48.         }  
  49.         return true;  
  50.     }  
  51. }  
编写测试程序:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class TestSet{  
  2.     public static void main(String args[]){  
  3.         Set<Person> hashSet = new HashSet<Person>();  
  4.         hashSet.add(new Person("马先生",22015));  
  5.         hashSet.add(new Person("李小姐",22018));  
  6.         hashSet.add(new Person("李先生",22020));  
  7.         Iterator<Person> it = hashSet.iterator();  
  8.         while(it.hasNext()){  
  9.             Person person = it.next();  
  10.             System.out.println(person.getName()+"   "+person.getId_card());  
  11.         }  
  12.     }  
  13. }  
程序的运行结果如下: 
李小姐  22018 
李先生  22020 
马先生  22015 
如果既想保留HashSet类快速定位集合中对象的优点,又想让集合中的对象按插入的顺序保存,可以通过HashSet类的子类LinkedHashSet实现Set集合。

2、LinkedHashSet


 LinkedHashSet,顾名思义,就是在Hash的实现上添加了Linked的支持。对于LinkedHashSet,在每个节点上通过一个链表串联起来,这样,就可以保证确定的顺序。对于希望有常量复杂度的高效存取性能要求、同时又要求排序的情况下,可以直接使用LinkedHashSet。

它实现了Set接口。存入Set的每个元素必须是唯一的,因为Set不保存重复元素。但是Set接口不保证维护元素的次序。LinkedHashSet具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的顺序),于是在使用迭代器便利Set时,结果会按元素插入的次序显示。

在HashSet中有一个构造方法,如下:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. HashSet(int initialCapacity, float loadFactor, boolean dummy) {  
  2.        map = new LinkedHashMap<>(initialCapacity, loadFactor);  
  3. }      
这个构造函数是包访问权限,且底层使用LinkedHashMap来实现,所以LinkedHashSet是通过调用这个构造函数来获取实例而保持元素的插入顺序的,源代码如下:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class LinkedHashSet<E>   extends HashSet<E>   implements Set<E>, Cloneable, java.io.Serializable {  
  2.   
  3.     private static final long serialVersionUID = -2851667679971038690L;  
  4.   
  5.     public LinkedHashSet(int initialCapacity, float loadFactor) {  
  6.         super(initialCapacity, loadFactor, true);  
  7.     }  
  8.     public LinkedHashSet(int initialCapacity) {  
  9.         super(initialCapacity, .75f, true);  
  10.     }  
  11.     public LinkedHashSet() {  
  12.         super(16, .75f, true);  
  13.     }  
  14.   
  15.     public LinkedHashSet(Collection<? extends E> c) {  
  16.         super(Math.max(2*c.size(), 11), .75f, true);  
  17.         addAll(c);  
  18.     }  
  19. }  
继续编写上一个测试程序,将
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Set<Person> hashSet = new HashSet<Person>();  
改为
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Set<Person> hashSet = new LinkedHashSet<Person>();  
后,输出结果发现,和插入的顺序一致。


3、TreeSet


TreeSet类不仅实现了Set接口,还实现了java.util.SortedSet接口,从而保证在遍历集合时按照递增的顺序获得对象。遍历对象时可能是按照自然顺序递增排列,因此存入用TreeSet类实现的Set集合的对象必须实现Comparable接口;也可能是按照指定比较器递增排序,即可以通过比较器对用TreeSet类实现的Set集合中的对象进行排序。 
下面先看 TreeSet 类的部分源代码:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{  
  2.     // 使用 NavigableMap 的 key 来保存 Set 集合的元素  
  3.     private transient NavigableMap<E,Object> m;  
  4.     // 使用一个 PRESENT 作为 Map 集合的所有 value  
  5.     private static final Object PRESENT = new Object();  
  6.       
  7.     //----------构造函数-------------------------------  
  8.     // 包访问权限的构造器,以指定的 NavigableMap 对象创建 Set 集合  
  9.     TreeSet(NavigableMap<E,Object> m) {  
  10.         this.m = m;  
  11.     }  
  12.     // 以自然排序方式创建      
  13.     public TreeSet() {  
  14.         this(new TreeMap<E,Object>());  
  15.     }  
  16.     // 以指定的排序方式创建  
  17.     public TreeSet(Comparator<? super E> comparator) {  
  18.         this(new TreeMap<>(comparator));  
  19.     }  
  20.   
  21.     public TreeSet(Collection<? extends E> c) {  
  22.         this();    // 调用无参的默认构造器  
  23.         addAll(c); // 添加Collection中已有的元素  
  24.     }  
  25.     // 以SortedSet为基础创建TreeSet  
  26.     public TreeSet(SortedSet<E> s) {  
  27.         this(s.comparator());  
  28.         addAll(s);  
  29.     }  
  30. }  

提供了获取部分元素的方法如下:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,E toElement,   boolean toInclusive) {  
  2.        return new TreeSet<>(m.subMap(fromElement, fromInclusive,toElement,   toInclusive));  
  3.    }  
  4.    public NavigableSet<E> headSet(E toElement, boolean inclusive) {  
  5.        return new TreeSet<>(m.headMap(toElement, inclusive));  
  6.    }  
  7.    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {  
  8.        return new TreeSet<>(m.tailMap(fromElement, inclusive));  
  9.    }  
  10.    public SortedSet<E> subSet(E fromElement, E toElement) {  
  11.        return subSet(fromElement, true, toElement, false);  
  12.    }  
  13.    public SortedSet<E> headSet(E toElement) {  
  14.        return headSet(toElement, false);  
  15.    }  
  16.    public SortedSet<E> tailSet(E fromElement) {  
  17.        return tailSet(fromElement, true);  
  18.    }  


从如上的几个构造器可以看出,其实底层还是通过TreeMap来实现的。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class Person implements Comparable{  
  2.     private String name;  
  3.     private long id_card;  
  4.     public Person(String name,long id_card){  
  5.         this.name = name;  
  6.         this.id_card = id_card;  
  7.     }  
  8.     public String getName(){  
  9.         return name;  
  10.     }  
  11.     public void setName(String name){  
  12.         this.name = name;  
  13.     }  
  14.     public long getId_card(){  
  15.         return id_card;  
  16.     }  
  17.     public void setId_card(long id_card){  
  18.         this.id_card = id_card;  
  19.     }  
  20.     public int compareTo(Object o){//默认按编号升序排序  
  21.         Person person = (Person)o;  
  22.         int result = id_card>person.id_card?1:(id_card==person.id_card?0:-1);  
  23.         return result;  
  24.     }  
  25. }  
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public class TestSet{  
  2.     public static void main(String args[]){  
  3.         TreeSet<Person> treeSet = new TreeSet<Person>();  
  4.         Person p1 = new Person("马先生",22015);  
  5.         Person p2 = new Person("李先生",22016);  
  6.         Person p3 = new Person("王小姐",22018);  
  7.         Person p4 = new Person("尹先生",22020);  
  8.         Person p5 = new Person("王先生",22012);  
  9.         treeSet.add(p1);  
  10.         treeSet.add(p2);  
  11.         treeSet.add(p3);  
  12.         treeSet.add(p4);  
  13.         treeSet.add(p5);  
  14.         System.out.println("初始化的集合:");  
  15.         Iterator<Person> it = treeSet.iterator();  
  16.         while(it.hasNext()){  
  17.             Person p = it.next();  
  18.             System.out.println(p.getId_card()+" "+p.getName());  
  19.         }  
  20.         System.out.println("截取前面部分得到的集合:");  
  21.         it = treeSet.headSet(p1).iterator();  
  22.         while(it.hasNext()){  
  23.             Person p = it.next();  
  24.             System.out.println(p.getId_card()+" "+p.getName());  
  25.         }  
  26.         System.out.println("截取中间部分得到的集合:");  
  27.         it = treeSet.subSet(p1,p3).iterator();  
  28.         while(it.hasNext()){  
  29.             Person p = it.next();  
  30.             System.out.println(p.getId_card()+" "+p.getName());  
  31.         }  
  32.         System.out.println("截取后面部分得到的集合:");  
  33.         it = treeSet.tailSet(p3).iterator();  
  34.         while(it.hasNext()){  
  35.             Person p = it.next();  
  36.             System.out.println(p.getId_card()+" "+p.getName());  
  37.         }  
  38.     }  
  39. }  
程序的运行结果如下: 
初始化的集合: 
22012   王先生 
22015   马先生 
22016   李先生 
22018   王小姐 
22020   尹先生 
截取前面部分得到的集合: 
22012   王先生 
截取中间部分得到的集合: 
22015   马先生 
22016   李先生 
截取后面部分得到的集合: 
22018   王小姐 
22020   尹先生 
在使用由TreeSet类实现的Set集合时,也可以通过单独的比较器对集合中的对象进行排序。 
比较器既可以作为一个单独的类,也可以作为对应类的内部类,本例中移内部类的形式实现比较器。 
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. import java.util.Comparator;  
  2. public class Person implements Comparable{  
  3.     private String name;  
  4.     private long id_card;  
  5.     public Person(String name,long id_card){  
  6.         this.name = name;  
  7.         this.id_card = id_card;  
  8.     }  
  9.     public String getName(){  
  10.         return name;  
  11.     }  
  12.     public void setName(String name){  
  13.         this.name = name;  
  14.     }  
  15.     public long getId_card(){  
  16.         return id_card;  
  17.     }  
  18.     public void setId_card(long id_card){  
  19.         this.id_card = id_card;  
  20.     }  
  21.     public int compareTo(Object o){//默认按编号升序排序  
  22.         Person person = (Person)o;  
  23.         int result = id_card>person.id_card?1:(id_card==person.id_card?0:-1);  
  24.         return result;  
  25.     }  
  26.     static class PersonComparator implements Comparator{  
  27.         public static final int NAME = 1;  
  28.         public static final int ID_CARD = 2;  
  29.         private int orderByColumn = 1;//默认为按姓名排序  
  30.         public static final boolean ASC = true;  
  31.         public static final boolean DESC = false;  
  32.         private boolean orderByMode = true;//默认为按升序排序  
  33.         public int compare(Object o1,Object o2){//实现Comparator接口的方法  
  34.             Person p1 = (Person)o1;  
  35.             Person p2 = (Person)o2;  
  36.             int result = 0;//默认的判断结果为两个对象相等  
  37.             switch(orderByColumn){//判断排序条件  
  38.                 case 1:  
  39.                     String s1 = CnToSpell.getFullSpell(p1.getName());  
  40.                     String s2 = CnToSpell.getFullSpell(p2.getName());   
  41.                     if(orderByMode){//升序  
  42.                         result = s1.compareTo(s2);  
  43.                     }  
  44.                     else{//降序  
  45.                         result = s2.compareTo(s1);  
  46.                     }  
  47.                     break;  
  48.                 case 2:  
  49.                     if(orderByMode){//升序  
  50.                         result = (int)(p1.getId_card()-p2.getId_card());  
  51.                     }  
  52.                     else{//降序  
  53.                         result = (int)(p2.getId_card()-p1.getId_card());  
  54.                     }  
  55.                     break;  
  56.             }  
  57.             return result;  
  58.         }  
  59.         public void orderByColumn(int orderByColumn){//用来设置排序条件  
  60.             this.orderByColumn = orderByColumn;  
  61.         }  
  62.         public void orderByMode(boolean orderByMode){//用来设置排序方式  
  63.             this.orderByMode = orderByMode;  
  64.         }  
  65.     }  
  66. }  
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. import java.util.*;  
  2. public class TestSet{  
  3.     public static void main(String args[]){  
  4.         TreeSet<Person> treeSet1 = new TreeSet<Person>();  
  5.         Person p1 = new Person("马先生",22015);  
  6.         Person p2 = new Person("李先生",22016);  
  7.         Person p3 = new Person("王小姐",22018);  
  8.         treeSet1.add(p1);  
  9.         treeSet1.add(p2);  
  10.         treeSet1.add(p3);  
  11.         System.out.println("客户化排序前,默认按编号升序排序:");  
  12.         //新创建一个Set集合,不进行客户化排序,默认按编号升序排序  
  13.         TreeSet<Person> treeSet2 = new TreeSet<Person>(treeSet1);//通过构造函数初始化集合  
  14.         Iterator<Person> it1 = treeSet2.iterator();  
  15.         while(it1.hasNext()){  
  16.             Person p = it1.next();  
  17.             System.out.println(p.getId_card()+" "+p.getName());  
  18.         }  
  19.         System.out.println("客户化排序后,按编号降序排序:");  
  20.         //新创建一个Set集合,进行客户化排序,客户化排序方式为按编号降序排序  
  21.         Person.PersonComparator pc = new Person.PersonComparator();//创建比较器(内部类)的实例  
  22.         pc.orderByColumn(Person.PersonComparator.ID_CARD);//设置排序依据的属性  
  23.         pc.orderByMode(Person.PersonComparator.DESC);//设置排序方式  
  24.         TreeSet<Person> treeSet3 = new TreeSet<Person>(pc);//必须通过构造函数设置比较器  
  25.         treeSet3.addAll(treeSet1);//初始化集合  
  26.         Iterator<Person> it2 = treeSet3.iterator();  
  27.         while(it2.hasNext()){  
  28.             Person p = it2.next();  
  29.             System.out.println(p.getId_card()+" "+p.getName());  
  30.         }  
  31.     }  
  32. }  
程序的运行结果如下: 
客户化排序前,默认按编号升序排序: 
22015   马先生 
22016   李先生 
22018   王小姐 
客户化排序后,按编号降序排序: 
22018   王小姐 
22016   李先生 
22015   马先生 

用LinkedHashSet取出ArrayList中的重复数据

因为LinkedHashSet是有序和不能重复的

public static void listTest1(){List<String> list = new ArrayList<String>();list.add("a");list.add("b");list.add("c");list.add("a");for(String s:list){//System.out.println(s);}list = function(list);for(String s :list){System.out.println(s);}}public static <E> List<E> function(List<E> list){return new ArrayList<E>(new LinkedHashSet<E>(list));}


0 0
原创粉丝点击