• 集合类小结


    一、数组与集合

      数组(可以存储对象包括基本类型)是一种常见的数据结构,对元素访问快速,但长度固定,存储数据类型需一致。

      集合只能存储对象,长度可变,可以存储不同类型的对象。

      数组与集合之间可以通过toArray()和Arrays.asList()相互转换。注:(asList返回的对象是一个Arrays内部类,并没有实现集合的add/remove/clear方法。)

    二、集合框架图

      集合类存放在java.util包中,主要两种类型:Collection和Map,继承关系如下图:

     

    List、Set和Map是集合中的三个主要接口。其中,List和Set继承自Collection(没有直接实现类)根接口,Map和Collection相互独立,但也属于集合类。

      List:有序且允许元素重复;基于数组的Vector和ArrayList适合查询,而基于链表的LinkedList适合添加,删除操作。

      Set:无序且不允许元素重复;不能重复的原因是:Set实现的基础是Map(HashMap),HashMap的key不能重复。

      Map:键值对集合,键不能重复,值可重复。

    三、集合类遍历

    1、Collection

    Collection没有get()方法来获取某个元素,只能通过iterator()遍历元素;foreach内部也是采用了Iterator的方式实现(循环体remove/add会导致expectedModCount和modCount不相等报异常);List可以通过get()方法获取元素,ListIterator是Iterator的子接口,专门用于List。

    //iterator
    Iterator iterator = list.iterator(); 
    while(iterator.hasNext()){ 
      int i = (Integer) iterator.next();                   
      System.out.println(i);   
    }
    
    //foreach
    for (Object object : list) {  
      System.out.println(object);      
    }
    
    //for
    for (int i = 0 ;i<list.size();i++) {      
      int v= (Integer) list.get(i);                     
      System.out.println(v);         
    } 
        

    2、Map

    1)通过Map.keySet()将Map中的所有key存入到Set集合中,然后通过iterator()获取迭代器或者foreach遍历key,最后通过Map.get(key)方法取得对应的值。

    //iterator
    Iterator it=map.keySet().iterator(); 
    while(it.hasNext()){ 
        String key; 
        String value; 
        key=it.next().toString(); 
        value=map.get(key); 
        System.out.println(key+"--"+value); 
    } 
    
    //foreach
    for (String key : map.keySet()) {
      System.out.println("key= " + key + " and value= " + map.get(key));
    }

    2)通过Map.entrySet()将键值对作为一个整体一对一对地存放到Set集合中,Map.Entry表示映射关系,然后通过iterator()获取迭代器或者foreach遍历获取键(e.getKey())和值(e.getValue());

    //iterator
    Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
    while (it.hasNext()) {
         Map.Entry<Integer, String> entry = it.next();
         System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }
    
    //foreach
    for (Map.Entry<Integer, String> entry : map.entrySet()) {
        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }

    3)通过Map.values()遍历所有的value,但不能遍历key。

    for (String v : map.values()) {
        System.out.println("value= " + v);
    }

    四、接口实现类的区别比较

    1、ArrayList和Vector

    1)同步性:Vector使用了synchronized方法,是同步和线程安全的,ArrayList是异步和线程非安全的(并发写易出现ConcurrentModificationException),可以通过List Collections.synchronizedList(List list)实现同步。

    2)数据增长:当元素个数超过容量时,Vector默认增长为原来的2倍,ArrayList为1.5倍;ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。

     2、ArrayList和LinkedList

    1)实现基础:ArrayList基于数组,LinkedList基于双向链表。

    2)效率:get和set操作,ArrayList占优;add和remove操作LinkedList占优。

    3、HashMap和Hashtable

    1)继承:HashMap继承于AbstractMap,Hashtable继承于Dictionary。

    public class HashMap<K,V> extends AbstractMap<K,V>
        implements Map<K,V>, Cloneable, Serializable
    
    public class Hashtable<K,V>
        extends Dictionary<K,V>
        implements Map<K,V>, Cloneable, java.io.Serializable

    2)同步性:Hashtable是同步的;HashMap是非同步的,可以通过Map Collections.synchronizedMap(Map m)实现;如需线程安全,推荐使用ConcurrentHashMap。

    3)键值要求:HashMap允许存在一个为null的key,多个为null的value ;Hashtable的key和value都不允许为null。

    4)遍历方式内部实现:Hashtable、HashMap都使用了 Iterator;由于历史原因,Hashtable还使用了Enumeration的方式 。

    5)默认大小和扩容方式:Hashtable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

    jdk1.7
    java.util.HashTable
    // 哈希表默认初始大小为11
    public Hashtable() {
        this(11, 0.75f);
    }
    
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;
    
        // 每次扩容为原来的2n+1
        int newCapacity = (oldCapacity << 1) + 1;
        // ...
    }
    
    
    java.util.HashMap
    // 哈希表默认初始大小为2^4=16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 每次扩充为原来的2n 
        if ((size >= threshold) && (null != table[bucketIndex])) {
           resize(2 * table.length);
    }

    6)Hashtable直接使用对象的hashCode,而HashMap重新计算hash值

    jdk1.7
    java.util.HashTable
    // hash 不能超过Integer.MAX_VALUE 所以要取其最小的31个bit int hash = hash(key); int index = (hash & 0x7FFFFFFF) % tab.length; // 直接计算key.hashCode() private int hash(Object k) { // hashSeed will be zero if alternative hashing is disabled. return hashSeed ^ k.hashCode(); } java.util.HashMap int hash = hash(key); int i = indexFor(hash, table.length); // 在计算了key.hashCode()之后,做了一些位运算来减少哈希冲突 final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } // 取模不再需要做除法 static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); }

    JDK7中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK8中采用的是位桶+链表/红黑树(有关红黑树请查看红黑树)的方式。当某个位桶的链表的长度达到某个阀值(8)的时候,这个链表就将转换成红黑树,改进链表过长查询慢的问题。另外,JDK8将链表头插改为尾插,避免并发死循环问题,当然仍然是非线程安全,比如数据丢失。

    7)Hashtable有一个contains(Object value),功能和containsValue(Object value)功能一样。

  • 相关阅读:
    74. Search a 2D Matrix(js)
    73. Set Matrix Zeroes(js)
    72. Edit Distance(js)
    71. Simplify Path(js)
    兼容各种浏览器版本的事件绑定函数?
    浏览器兼容性问题
    柯里化函数
    token登录验证机制
    vue懒加载
    在Vue中使用了Swiper ,动态从后台获取数据的之后,swiper滑动失效??
  • 原文地址:https://www.cnblogs.com/aaron-shu/p/6720238.html
Copyright © 2020-2023  润新知