Java中的集合,工具类,泛型

    常用集合体系图:                    
                                      
Iterable
    |
    |--Collection
    |------List
    |------ArrayList
    |------LinkedList
    |------Vector
    |
    |--Set
    |------HashSet
    |------LinkedHashSet
    |------TreeSet
    |
    |--Map
    |------HashMap
    |----------LinkedHashMap
    |------ Hashtable
    |----------Properties
    |------SortedMap
    |-----------TreeMap



优点:
  1. 集合不用指定长度,它可以自动扩容。
  2. 可以保存任意类型的元素。

一,Collection
    常见方法:
    * add/addAll  可以添加元素或者对象
    * remove/removeAll
    * contains/containsAll
    * clear
    * size 实际集合的大小
    * isEmpty 是否为空

    注意:
    在使用Iterator的过程中,默认不能做增删,会影响迭代器的指针指空, 报异常ConCurrentModificationException。如果非要做增删可以使用迭代器本身的remove方法。


     Collection的遍历方式
  1. 迭代器遍历
        Iterator iterator = new ArrayList().iterator();
        //判断,如果返回true,则进行下一步
        while(iterator.hasNext()){
                //下移一位,并获取对应元素
                System.out.println(iterator.next());
        }

  1. foreach遍历    
        for(Object o: col){
            System.out.println(o);
        }
    显然后者要简洁一些。


二,List 的常用方法
     * add(object)增
     * remove(index)按指定索引删
     * set(index,object)改
     * indexOf(object)查
     * add(index,object)插入
     * get(index)获取

    特点:
    有序,可重,可变数组

    遍历方式:
    由于List是有序的(有一个整数索引记录了插入的位置),所以List可以使用for循环来遍历。
    还可以使用Collection的两种遍历方式。

    
三,ArrayList实现List接口

    常用的方法跟List的一样,线程不安全
    维护了Object[] elementData 初始值为0;
    如果添加元素时容量不够首次会给10个空间,然后会扩容到原来的1.5倍。
    
    由于ArrayList是有序的,查询的时候相对索引的速度较快,所以改查的效率较高。


四,Verto 实现 List 接口
    跟ArrayList很像,线程安全,扩容到原来的2倍。

    

五,LinkedList 实现 List 接口
    底层是双向链表。
    维护first,和last 两个重要的属性
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node first;
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node last;
每个节点(Node)维护三个属性item,prev,next 。
    private static class Node {
        E item;
        Node next;
        Node prev;
        Node(Node prev, E element, Node next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

最终实现链表结构!

由于 LinkedList 是链表的结构,在删除元素的时候不需要把应删除元素后面的元素前移,只需将应删除元素前后元素的指针改变指向即可。


    -----------综上所诉----------
    如果考虑线程安全问题:Vector
    不考虑线程安全问题:
                        查找较多:ArrayList
                        增删较多:LinkedList


六,Set集合

    特点:
    无序,不可重。

    没有特定的方法,从Coolection继承。
    遍历方式与Collection相同(两种)


七,HashSet实现Set接口
    无序,不可重。
    底层是基于HashMap的结构
    通过调用HashCode方法得到元素的哈希码,然后通过equals比较两个相同哈希码的元素,实现去重。

    如果想要自定义去重,只需重写HashCode,equals方法。


八,TreeSet 实现 Set接口
    无序,不可重
    底层是基于TreeMap,而TreeMap基于红黑树结构(二叉树的一种,可以尽可能的调整为平衡二叉树)实现元素的排序。

    排序:
    1.自然排序
        必须让添加的元素实现Comparable接口,实现CompareTo方法
    2.定制排序
        创建TreeSet对象时,传入一个Comparator接口的对象,并实现里面的compare方法,同时 可以通过比较方法的返回值是否为0来判断是否重复 
            TreeSet set = new TreeSet(new Comparator(){

                @Override
                public int compare(Object o1, Object o2) 
          
                    Book b1 = (Book) o1;
                    Book b2 = (Book) o2;
                    return Double.compare(b2.getPrice (),b1.getPrice());
                }
            });




九, Map

    用于保存一组键值对的隐射关系,其中键不能重复,但是值可以重复。一个键对应一个值。

    常见方法:
    put 添加
    remove删除
    containsKey查找键
    containsValue查找值
    get根据键获取值
    size获取键值对的个数
    isEmpty判断元素是否为空
    clear清除
    entrySet 获取所有的关系
    keySet获取所有的键
    values获取所有的值

    两种遍历方式:
    都是先转换为Set对象  -->     Set entrys = map.entrySet();

    一种是使用迭代器Iterator的  hasNext,  next  方法进行循环遍历。
    
    另一种是使用foreac循环遍历
        //方式1:通过entryset
        
        Set entrys = map.entrySet();
        
        for (Object object : entrys) {
            Map.Entry entry = (Entry) object;
            
            System.out.println(entry.getKey()+"的工资是:"+entry.getValue());
            
        }
        System.out.println("---------------------------");
        
        //方式2:通过keySet
        
        Set keys = map.keySet();
        
        for (Object key : keys) {
            System.out.println(key+"的工资是:"+map.get(key));
        }

    
十,HashMap实现 Map接口
    底层:哈希表。数组+链表+红黑树
    维护Node类型的数组table

    源码分析:
    当HashMap创建对象时,初始化LoadFactor为0.75(最优的离散值),table数组默认为null;
    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
当第一次添加时,初始化table的容量为16,临界值为12(16*0.74)
    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node[] resize() {
        Node[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        .
        .
        .
        return newTab;
    }

然后调用putVal方法

①先获取key的二次哈希值并进行取与运算,得出存放的位置
②判断该存放位置上是否有元素,如果没有直接存放
              如果该存放位置上已有元素,则进行继续判断:
                                    如果和当前元素直接相等,则覆盖
                                    如果不相等,则继续判断是否是链表结构还是树状结构,按照对应结构的判断方式判断相等
③将size更新,判断是否超过了临界值,如果超过了,则需要重新resize()进行2倍扩容,并打乱原来的顺序,重新排列
    /**
     * Computes key.hashCode() and spreads (XORs) higher bits of hash
     * to lower.  Because the table uses power-of-two masking, sets of
     * hashes that vary only in bits above the current mask will
     * always collide. (Among known examples are sets of Float keys
     * holding consecutive whole numbers in small tables.)  So we
     * apply a transform that spreads the impact of higher bits
     * downward. There is a tradeoff between speed, utility, and
     * quality of bit-spreading. Because many common sets of hashes
     * are already reasonably distributed (so don't benefit from
     * spreading), and because we use trees to handle large sets of
     * collisions in bins, we just XOR some shifted bits in the
     * cheapest possible way to reduce systematic lossage, as well as
     * to incorporate impact of the highest bits that would otherwise
     * never be used in index calculations because of table bounds.
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);          //二次哈希 取与 (二进制都为1,则结果为1)
    }
    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node[] tab; Node p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)                       //判断数组是否为空或已满,空则进行初始赋值,满则扩容
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)                                //判断数组是否为空  空则直接添加节点
            tab[i] = newNode(hash, key, value, null);
        else {
            Node e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))            
                e = p;                                                             //相同元素直接覆盖
            else if (p instanceof TreeNode)
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);    //如果是树结构,按照树结构进行添加
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {                                   //没有相同元素,直接添加在最后面
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st      //判断桶中的节点数是否可以构建树
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))    //相同则退出循环
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)                                                      //为了保证哈希表内元素间适当的离散性,当size达到临界点时需要扩容,并重新分配排列
            resize();
        afterNodeInsertion(evict);
        return null;
    }
红黑树相当耗费内存,影响程序效率,在HashMap中链表变成红黑树概率极小。
只有当一个桶中的链表的节点数>=8 &&  桶的总个数(table的容量)>=64时,会将链表结构变成红黑树结构

了解:
jdk7和jdk8的区别
        1.jdk7:创建HashMap对象时,则初始table容量为16
          jdk8:创建HashMap对象时,没有初始table,仅仅只是初始加载因子。只有当第一次添加时才会初始table容量为16.

        2.jdk7:table的类型为Entry
          jdk8:table的类型为Node

        3.jdk7:哈希表为数组+链表,不管链表的总结点数是多少都不会变成树结构
          jdk8:哈希表为数组+链表+红黑树,当链表的节点数>=8 &&  桶的总个数(table的容量)>=64时,会将链表结构变成红黑树结构



十一,Hashtable实现 Map接口
    底层结构:哈希表    线程安全   不允许null键null值。



十二,TreeMap实现 Map接口
    底层结构:红黑树,可以实现对添加元素的key进行排序
    应用:
        自然排序:要求key的元素类型实现 Comparable ,并实现里面的 compareTo 方法

        定制排序:要求创建TreeMap对象时,传入 Comparator 比较器对象,并实现里面的 compare 方法


十三,Collections工具类的学习
    常见方法:
    reverse  反转
    sort  排序
    swap  两各索引处元素的交换
    shuffle  随机打乱顺序
    max  获取最大值
    min   获取最小值
    frequency  查找指定元素出现的次数
    replaceAll   替换旧值为新值
    copy    复制,注意:新集合的size>旧集合的size


十四,泛型
    参数化的类型。可以将某个类型当做参数传递给类、接口或方法中。不传默认为Object类型

    好处
        1、编译时检查待添加的元素类型,提高了类型的安全性
        2、减少了类型转换的次数,提高了效率
         3、减少了编译警告



















你可能感兴趣的:(North,drift)