zoukankan      html  css  js  c++  java
  • Set集合学习

    Java中的Set主要有:HashSet、TreeSet、LinkedHashSet

    一:HashSet    

      HashSet 是一个没有重复元素的无序集合。 HashSet由HashMap实现的,不保证元素的顺序,允许使用null元素,一系列操作的实现都是调用其底层的map的方法而已。

      1:HashSet元素的存储顺序:由于HashSet底层是用HashMap存储元素的,每组数据都没有索引,很多list可用的方法他都没有,凡是需要通过索引来进行操作的方法都没有,也不能使用普通for循环来进行遍历,只有加强型for和迭代器两种遍历方法

           2:HashSet如何保证元素不重复:HashSet在调用add(obj)方法插入元素时,首先调用元素的hashcode()方法,如果map中对应索引位还没有内容,则把元素值储存;如果索引位已有内容,则继续调用 元素的equals()方法把新增元素与已有元素值进行比较,如果是相等的,则新值不插入(因为重复了),如果是不相等的,则把新元素插入到该索引位元素列表的末尾。【原因:哈希码是存在冲突的,我们只规定了相等对象必定有相同哈希码,却没有规定不同对象不能有相同哈希码。当不同对象拥有同一哈希码时,就只能在哈希码索引位上建立链表来存放了。】

    主要源码:(基于JDK1.6.0_45)

      1  package java.util;
      2  public class HashSet<E>
      3   extends AbstractSet<E>
      4   implements Set<E>, Cloneable, java.io.Serializable
      5  {
      6 
      7   static final long serialVersionUID = -5024744406713321676L;
      8    // 使用 HashMap 的 key 保存 HashSet 中所有元素  
      9   private transient HashMap<E,Object> map;
     10   //定义一个虚拟的 Object 对象作为 HashMap 的 value 
     11   private static final Object PRESENT = new Object();
     12 
     13   // 默认构造函数
     14   public HashSet() {
     15    // 调用HashMap的默认构造函数,创建map
     16    map = new HashMap<E,Object>();
     17   }
     18   // 带集合的构造函数
     19   public HashSet(Collection<? extends E> c) {
     20   
     21    // HashMap的加载因子是0.75,(c.size()/.75f) + 1 正好是总的空间大小。   
     22    // 指定为16是从性能考虑,2的指数倍,避免重复计算。
     23    map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
     24    // 将集合(c)中的全部元素添加到HashSet中
     25    addAll(c);
     26   }
     27   // 指定HashSet初始容量和加载因子的构造函数
     28   public HashSet(int initialCapacity, float loadFactor) {
     29    map = new HashMap<E,Object>(initialCapacity, loadFactor);
     30   }
     31   // 指定HashSet初始容量的构造函数
     32   public HashSet(int initialCapacity) {
     33    map = new HashMap<E,Object>(initialCapacity);
     34   }
     35   HashSet(int initialCapacity, float loadFactor, boolean dummy) {
     36    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
     37   }
     38   // 返回HashSet的迭代器
     39   public Iterator<E> iterator() {
     40    // 实际上返回的是HashMap的“key集合的迭代器”
     41    return map.keySet().iterator();
     42   }
     43   public int size() {
     44    return map.size();
     45   }
     46   public boolean isEmpty() {
     47    return map.isEmpty();
     48   }
     49   public boolean contains(Object o) {
     50    return map.containsKey(o);
     51   }
     52   // 将元素(e)添加到HashSet中
     53   public boolean add(E e) {
     54    return map.put(e, PRESENT)==null;
     55   }
     56   // 删除HashSet中的元素(o)
     57   public boolean remove(Object o) {
     58    return map.remove(o)==PRESENT;
     59   }
     60   public void clear() {
     61    map.clear();
     62   }
     63   // 克隆一个HashSet,并返回Object对象
     64   public Object clone() {
     65    try {
     66     HashSet<E> newSet = (HashSet<E>) super.clone();
     67     newSet.map = (HashMap<E, Object>) map.clone();
     68     return newSet;
     69    } catch (CloneNotSupportedException e) {
     70     throw new InternalError();
     71    }
     72   }
     73   // java.io.Serializable的写入函数
     74   // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中
     75   private void writeObject(java.io.ObjectOutputStream s)
     76    throws java.io.IOException {
     77    // Write out any hidden serialization magic
     78    s.defaultWriteObject();
     79    // Write out HashMap capacity and load factor
     80    s.writeInt(map.capacity());
     81    s.writeFloat(map.loadFactor());
     82    // Write out size
     83    s.writeInt(map.size());
     84    // Write out all elements in the proper order.
     85    for (Iterator i=map.keySet().iterator(); i.hasNext(); )
     86     s.writeObject(i.next());
     87   }
     88   // java.io.Serializable的读取函数
     89   // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出
     90   private void readObject(java.io.ObjectInputStream s)
     91    throws java.io.IOException, ClassNotFoundException {
     92    // Read in any hidden serialization magic
     93    s.defaultReadObject();
     94    // Read in HashMap capacity and load factor and create backing HashMap
     95    int capacity = s.readInt();
     96    float loadFactor = s.readFloat();
     97    map = (((HashSet)this) instanceof LinkedHashSet ?
     98     new LinkedHashMap<E,Object>(capacity, loadFactor) :
     99     new HashMap<E,Object>(capacity, loadFactor));
    100    // Read in size
    101    int size = s.readInt();
    102    // Read in all elements in the proper order.
    103    for (int i=; i<size; i++) {
    104     E e = (E) s.readObject();
    105     map.put(e, PRESENT);
    106    }
    107   }
    108  }
    View Code

    HashSet主要的API

    boolean         add(E object)
    void            clear()
    Object          clone()
    boolean         contains(Object object)
    boolean         isEmpty()
    Iterator<E>     iterator()
    boolean         remove(Object object)
    int             size()

    HashSet与Map关系如下图:

    从图中可以看出:
      (01) HashSet继承于AbstractSet,并且实现了Set接口。
      (02) HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,HashSet的操作函数,实际上都是通过map实现的。

    HashSet遍历方式

    遍历实例:

    import java.util.Random;
    import java.util.Iterator;
    import java.util.HashSet;
    
    /*
     * @desc 介绍HashSet遍历方法
     *
     * @author hdb
     */
    public class HashSetIteratorTest {
    
        public static void main(String[] args) {
            // 新建HashSet
            HashSet set = new HashSet();
    
            // 添加元素 到HashSet中
            for (int i=0; i<5; i++){
            set.add(""+i);
          }
    // 通过Iterator遍历HashSet iteratorHashSet(set) ; // 通过for-each遍历HashSet foreachHashSet(set); } /* * 通过Iterator遍历HashSet推荐方式.
    * 第一步:根据iterator()获取HashSet的迭代器
    * 第二步:遍历迭代器获取各个元素。
    */ private static void iteratorHashSet(HashSet set) { for(Iterator iterator = set.iterator(); iterator.hasNext(); ) { System.out.printf("iterator : %s ", iterator.next()); }
         //另一种写法
         //Iterator iterator = set.iterator();
         //while (iterator.hasNext()) {
    // System.out.printf("iterator : %s ", iterator.next());
        
    //}
     } /* * 通过for-each遍历HashSet。不推荐!此方法需要先将Set转换为数组
       * 第一步:根据toArray()获取HashSet的元素集合对应的数组
    * 第二步:遍历数组,获取各个元素
    */ private static void foreachHashSet(HashSet set) { String[] arr = (String[])set.toArray(new String[0]); for (String str:arr){
             System.out.printf("for each : %s ", str);
          }
    } }

    实例学习如何使用HashSet

    import java.util.Iterator;
    import java.util.HashSet;
    
    /*
     * @desc HashSet常用API的使用。
     *
     * @author hdb
     */
    public class HashSetTest {
    
        public static void main(String[] args) {
            // HashSet常用API
            testHashSetAPIs() ;
        }
    
        /*
         * HashSet除了iterator()和add()之外的其它常用API
         */
        private static void testHashSetAPIs() {
    
            // 新建HashSet
            HashSet set = new HashSet();
    
            // 将元素添加到Set中
            set.add("a");
            set.add("b");
            set.add("c");
            set.add("d");
            set.add("e");
    
            // 打印HashSet的实际大小
            System.out.printf("size : %d
    ", set.size());
    
            // 判断HashSet是否包含某个值
            System.out.printf("HashSet contains a :%s
    ", set.contains("a"));
            System.out.printf("HashSet contains g :%s
    ", set.contains("g"));
    
            // 删除HashSet中的“e”
            set.remove("e");    // 新建一个包含b、c、f的HashSet
            HashSet otherset = new HashSet();
            otherset.add("b");
            otherset.add("c");
            otherset.add("f");
    
            // 克隆一个removeset,内容和set一模一样
            HashSet removeset = (HashSet)set.clone();
            // 删除“removeset中,属于otherSet的元素”
            removeset.removeAll(otherset);
            // 打印removeset
            System.out.printf("removeset : %s
    ", removeset);
    
            // 克隆一个retainset,内容和set一模一样
            HashSet retainset = (HashSet)set.clone();
            // 保留“retainset中,属于otherSet的元素”
            retainset.retainAll(otherset);
            // 打印retainset
            System.out.printf("retainset : %s
    ", retainset);
    
    
            // 遍历HashSet,推荐方式
            for(Iterator iterator = set.iterator();
                   iterator.hasNext(); ) {
            System.out.printf("iterator : %s ", iterator.next());
          }

         // 将Set转换为数组,遍历HashSet,不推荐
         String[] arr = (String[])set.toArray(new String[0]);
         for (String str:arr) {
            System.out.printf("for each : %s ", str); 
          }       
    // 清空HashSet set.clear(); // 输出HashSet是否为空 System.out.printf("%s ", set.isEmpty()?"set is empty":"set is not empty"); } }

    HashSet线程同步问题

    HashSet是非同步的。如果多个线程同时访问一个HashSet,而其中至少一个线程修改了该HashSet,那么它必须保持外部同步。通常是通过对自然封装该HashSet的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
    Set s = Collections.synchronizedSet(new HashSet(...));

    HashSet通过iterator()返回的迭代器是fail-fast的。

    二:TreeSet

        TreeSet在保持元素唯一性的基础上,更增加了使插入元素有序的特性。TreeSet的底层实际上是TreeMap,在TreeSet上的操作的实现都是调用了底层的TreeMap的方法。

      1:TreeSet的排序规则制定:

               1)元素自身具备比较性:元素类实现Comparable接口,重写compareTo方法,这种方式叫做元素的自然排序(默认排序)。

               2)在创建TreeSet时指定比较器:定义一个比较器类实现接口Comparator,重写compare方法,在创建TreeSet时把比较器对象传进去。

           2:TreeSet中元素的唯一性保证:通过元素的compareTo方法者treeset创建时的比较器compare方法,如果return 0则说明有相等元素存在,则新元素不插入。

    部分主要源码:

      1 package java.util;
      2 
      3 public class TreeSet<E> extends AbstractSet<E>
      4     implements NavigableSet<E>, Cloneable, java.io.Serializable
      5 {
      6     // NavigableMap对象
      7     private transient NavigableMap<E,Object> m;
      8 
      9     // TreeSet是通过TreeMap实现的,
     10     // PRESENT是键-值对中的值。
     11     private static final Object PRESENT = new Object();
     12 
     13     // 不带参数的构造函数。创建一个空的TreeMap
     14     public TreeSet() {
     15         this(new TreeMap<E,Object>());
     16     }
     17 
     18     // 将TreeMap赋值给 "NavigableMap对象m"
     19     TreeSet(NavigableMap<E,Object> m) {
     20         this.m = m;
     21     }
     22 
     23     // 带比较器的构造函数。
     24     public TreeSet(Comparator<? super E> comparator) {
     25         this(new TreeMap<E,Object>(comparator));
     26     }
     27 
     28     // 创建TreeSet,并将集合c中的全部元素都添加到TreeSet中
     29     public TreeSet(Collection<? extends E> c) {
     30         this();
     31         // 将集合c中的元素全部添加到TreeSet中
     32         addAll(c);
     33     }
     34 
     35     // 创建TreeSet,并将s中的全部元素都添加到TreeSet中
     36     public TreeSet(SortedSet<E> s) {
     37         this(s.comparator());
     38         addAll(s);
     39     }
     40 
     41     // 返回TreeSet的顺序排列的迭代器。
     42     // 因为TreeSet时TreeMap实现的,所以这里实际上时返回TreeMap的“键集”对应的迭代器
     43     public Iterator<E> iterator() {
     44         return m.navigableKeySet().iterator();
     45     }
     46 
     47     // 返回TreeSet的逆序排列的迭代器。
     48     // 因为TreeSet时TreeMap实现的,所以这里实际上时返回TreeMap的“键集”对应的迭代器
     49     public Iterator<E> descendingIterator() {
     50         return m.descendingKeySet().iterator();
     51     }
     52 
     53     // 返回TreeSet的大小
     54     public int size() {
     55         return m.size();
     56     }
     57 
     58     // 返回TreeSet是否为空
     59     public boolean isEmpty() {
     60         return m.isEmpty();
     61     }
     62 
     63     // 返回TreeSet是否包含对象(o)
     64     public boolean contains(Object o) {
     65         return m.containsKey(o);
     66     }
     67 
     68     // 添加e到TreeSet中
     69     public boolean add(E e) {
     70         return m.put(e, PRESENT)==null;
     71     }
     72 
     73     // 删除TreeSet中的对象o
     74     public boolean remove(Object o) {
     75         return m.remove(o)==PRESENT;
     76     }
     77 
     78     // 清空TreeSet
     79     public void clear() {
     80         m.clear();
     81     }
     82 
     83     // 将集合c中的全部元素添加到TreeSet中
     84     public  boolean addAll(Collection<? extends E> c) {
     85         // Use linear-time version if applicable
     86         if (m.size()==0 && c.size() > 0 &&
     87             c instanceof SortedSet &&
     88             m instanceof TreeMap) {
     89             SortedSet<? extends E> set = (SortedSet<? extends E>) c;
     90             TreeMap<E,Object> map = (TreeMap<E, Object>) m;
     91             Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
     92             Comparator<? super E> mc = map.comparator();
     93             if (cc==mc || (cc != null && cc.equals(mc))) {
     94                 map.addAllForTreeSet(set, PRESENT);
     95                 return true;
     96             }
     97         }
     98         return super.addAll(c);
     99     }
    100 
    101     // 返回子Set,实际上是通过TreeMap的subMap()实现的。
    102     public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
    103                                   E toElement,   boolean toInclusive) {
    104         return new TreeSet<E>(m.subMap(fromElement, fromInclusive,
    105                                        toElement,   toInclusive));
    106     }
    107 
    108     // 返回Set的头部,范围是:从头部到toElement。
    109     // inclusive是是否包含toElement的标志
    110     public NavigableSet<E> headSet(E toElement, boolean inclusive) {
    111         return new TreeSet<E>(m.headMap(toElement, inclusive));
    112     }
    113 
    114     // 返回Set的尾部,范围是:从fromElement到结尾。
    115     // inclusive是是否包含fromElement的标志
    116     public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
    117         return new TreeSet<E>(m.tailMap(fromElement, inclusive));
    118     }
    119 
    120     // 返回子Set。范围是:从fromElement(包括)到toElement(不包括)。
    121     public SortedSet<E> subSet(E fromElement, E toElement) {
    122         return subSet(fromElement, true, toElement, false);
    123     }
    124 
    125     // 返回Set的头部,范围是:从头部到toElement(不包括)。
    126     public SortedSet<E> headSet(E toElement) {
    127         return headSet(toElement, false);
    128     }
    129 
    130     // 返回Set的尾部,范围是:从fromElement到结尾(不包括)。
    131     public SortedSet<E> tailSet(E fromElement) {
    132         return tailSet(fromElement, true);
    133     }
    134 
    135     // 返回Set的比较器
    136     public Comparator<? super E> comparator() {
    137         return m.comparator();
    138     }
    139 
    140     // 返回Set的第一个元素
    141     public E first() {
    142         return m.firstKey();
    143     }
    144 
    145     // 返回Set的最后一个元素
    146     public E first() {
    147     public E last() {
    148         return m.lastKey();
    149     }
    150 
    151     // 返回Set中小于e的最大元素
    152     public E lower(E e) {
    153         return m.lowerKey(e);
    154     }
    155 
    156     // 返回Set中小于/等于e的最大元素
    157     public E floor(E e) {
    158         return m.floorKey(e);
    159     }
    160 
    161     // 返回Set中大于/等于e的最小元素
    162     public E ceiling(E e) {
    163         return m.ceilingKey(e);
    164     }
    165 
    166     // 返回Set中大于e的最小元素
    167     public E higher(E e) {
    168         return m.higherKey(e);
    169     }
    170 
    171     // 获取第一个元素,并将该元素从TreeMap中删除。
    172     public E pollFirst() {
    173         Map.Entry<E,?> e = m.pollFirstEntry();
    174         return (e == null)? null : e.getKey();
    175     }
    176 
    177     // 获取最后一个元素,并将该元素从TreeMap中删除。
    178     public E pollLast() {
    179         Map.Entry<E,?> e = m.pollLastEntry();
    180         return (e == null)? null : e.getKey();
    181     }
    182 
    183     // 克隆一个TreeSet,并返回Object对象
    184     public Object clone() {
    185         TreeSet<E> clone = null;
    186         try {
    187             clone = (TreeSet<E>) super.clone();
    188         } catch (CloneNotSupportedException e) {
    189             throw new InternalError();
    190         }
    191 
    192         clone.m = new TreeMap<E,Object>(m);
    193         return clone;
    194     }
    195 
    196     // java.io.Serializable的写入函数
    197     // 将TreeSet的“比较器、容量,所有的元素值”都写入到输出流中
    198     private void writeObject(java.io.ObjectOutputStream s)
    199         throws java.io.IOException {
    200         s.defaultWriteObject();
    201 
    202         // 写入比较器
    203         s.writeObject(m.comparator());
    204 
    205         // 写入容量
    206         s.writeInt(m.size());
    207 
    208         // 写入“TreeSet中的每一个元素”
    209         for (Iterator i=m.keySet().iterator(); i.hasNext(); )
    210             s.writeObject(i.next());
    211     }
    212 
    213     // java.io.Serializable的读取函数:根据写入方式读出
    214     // 先将TreeSet的“比较器、容量、所有的元素值”依次读出
    215     private void readObject(java.io.ObjectInputStream s)
    216         throws java.io.IOException, ClassNotFoundException {
    217         // Read in any hidden stuff
    218         s.defaultReadObject();
    219 
    220         // 从输入流中读取TreeSet的“比较器”
    221         Comparator<? super E> c = (Comparator<? super E>) s.readObject();
    222 
    223         TreeMap<E,Object> tm;
    224         if (c==null)
    225             tm = new TreeMap<E,Object>();
    226         else
    227             tm = new TreeMap<E,Object>(c);
    228         m = tm;
    229 
    230         // 从输入流中读取TreeSet的“容量”
    231         int size = s.readInt();
    232 
    233         // 从输入流中读取TreeSet的“全部元素”
    234         tm.readTreeSet(size, s, PRESENT);
    235     }
    236 
    237     // TreeSet的序列版本号
    238     private static final long serialVersionUID = -2479143000061671589L;
    239 }
    View Code

    三:LinkedHashSet

        LinkedHashSet的特性是:记录了元素的插入顺序。在遍历时可以按照元素的插入顺序进行遍历。

        LinkHashSet维护了一个双向链表,记录元素的插入顺序,然后再根据元素值,采用hashcode()、equals()方法来存储元素值并实现元素的唯一性。

  • 相关阅读:
    双六游戏 扩展欧几里得
    线段上的格点 辗转相除法(GCD)
    POJ 2395 Out of Hay
    POJ 2377 Bad Cowtractors
    POJ 1258 Agri-Net
    POJ 3169 Layout
    POJ 3723 Conscription
    次最短路径 POJ 3255 Roadblocks
    最小生成树求法 Prim + Kruskal
    如何将页脚固定在页面底部,4中方法 转载自:W3CPLUS
  • 原文地址:https://www.cnblogs.com/huangdabing/p/9249238.html
Copyright © 2011-2022 走看看