day18

 一、增加一个接口:java.lang.Iterable接口
   JDK1.5增加
   它有一个抽象方法:Iterator iterator() 
   实现这个接口允许对象成为 "foreach" 语句的目标
   Collection从JDK1.5之后开始继承Iterable接口。

二、java.util.Iterator接口在哪里实现的?
   //左边是Iterator接口,说明右边一定创建了一个Iterator接口的实现类对象,否则iterator调用方法是没有方法体可以执行
    Iterator iterator = list.iterator();
    跟踪源代码:list.iterator()
      public Iterator<E> iterator() {
        return new Itr();//Itr是一个内部类
     }
    跟踪每一种Collection的集合发现,所有的集合在内部都一个内部类来实现java.util.Iterator接口。
   为什么?
   (1)每一种集合的内部实现物理结构不一样,有的是数组有的是链表等,迭代方式不一样,无法给出统一的实现
   (2)迭代的作用,为某个集合迭代,遍历元素,那么它只为某类集合服务,迭代时又需要访问集合的内部(private)的元素, 所以我们设计为内部类更合适。
Set:是Collection另一个子接口
     一个不包含重复元素的 collection。
     更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
     正如其名称所暗示的,此接口模仿了数学上的 set 抽象。 
     Set的方法都是继承自Collection。
Set的实现类们:
    1、HashSet:元素是完全无序的,它的顺序是由hash值来决定的。
         HashSet:元素的存储和是否重复要依赖于元素的:hashCode()和equals()方法
    2、TreeSet:元素是按照“大小”顺序排列的,和添加顺序无关。
         TreeSet:元素的存储和是否重复要依赖于元素的比较大小的方法:int compareTo(Object o)或指定的比较器的int compare(Object o1,Object o2)
    3、LinkedHashSet:是HashSet的子类,但是比HashSet要多一个“记录元素添加顺序的功能”,因此它添加和删除的效率比HashSet相对低。
         当你既想要元素不重复,又想要按照添加的顺序时,选择LinkedHashSet。如果你只是想要不重复,那么选择HashSet,如果你执行想要添加顺序,选ArrayList等。
TreeSet:
    (1)按照元素的自然顺序排列:要求元素必须实现java.lang.Comparable接口
       例如:String实现了java.lang.Comparable接口,
       它是按照字符的Unicode编码值来排序。
   (2)按照指定的定制比较器的规则排序,要求指定一个java.util.Comparator接口的实现类对象
        例如:专门为比较String对象,定制了一个比较器java.text.Collator实现了java.util.Comparator接口

Map:

   是不同于Collection一种集合,它是用来存储“键值对——映射关系——(key,value)”
   将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
   Map接口的常用方法:
   1、添加
     put(Object key, Object value)
     任意Java的引用数据类型都可以作为key和value的类型,但是最常见的是Integer和String比较多的作为key的类型。
     putAll(Map map):把另一个map集合的映射关系添加到当前Map中
   2、获取映射关系的对数
     int size()
   3、查找
     boolean containsKey(Object key)  
     boolean containsValue(Object value) 
     V get(Object key) :根据key获取value
    boolean isEmpty() 
  4、删除
    V remove(Object key) :根据key移除一对映射关系,并且返回对应的value
    void clear()  
  5、遍历
  (1)Set keySet():
          因为Set不允许有重复元素,因为Map的所有key也是要求不允许重复的。
  (2)Collection values()
         因为value是可以重复的,所以这里不能用Set
  (3)Set entrySet()
         因为key不重复,所以entry是不重复的
        Entry类型是Map中的一个内部接口类型,映射项(键-值对)。它的实现类在Map的实现类中,以内部类的形式存在。

Map接口的实现类们:
   1、HashMap:哈希表
     如何确定key的不重复和顺序的:依据key的 hashCode 方法和 equals 方法
   2、TreeMap
     如何确定key的不重复和顺序的:依据key的自然顺序或定制比较器的规则
     因此要求要么key类型实现了java.lang.Comparable接口,并重写int compareTo(Object obj)
     要么在创建TreeMap时指定定制比较器对象(它是实现java.util.Comparator接口,并重写int compare(Object o1, Object o2)的对象)
   3、LinkedHashMap
     LinkedHashMap是HashMap的子类,比HashMap多维护了“添加”的顺序。
   4、Hashtable:哈希表
     如何确定key的不重复和顺序的: hashCode 方法和 equals 方法
   5、Properties
     Properties是Hashtable的子类,是一种特殊的集合,特殊在它的key和value的类型都是String
     HashMap和Hashtable:
     Hashtable:旧版的,线程安全的,不允许key和value为null值
     HashMap:新版的,线程不安全的,运行key和value为null值
 关系:
   HashSet和HashMap:HashSet其实就是一个HashMap
     添加到HashSet中的元素是作为HashMap的key,他们的value是共享了一个PRESENT(是Object类型的常量对象)
   TreeSet和TreeMap:TreeSet其实就是一个TreeMap
     添加到TreeSet中的元素是作为TreeMap的key,他们的value是共享了一个PRESENT(是Object类型的常量对象)
   LinkedHashSet和LinkedHashMap:LinkedHashSet其实就是一个LinkedHashMap
     添加到LinkedHashSet中的元素是作为LinkedHashMap的key,他们的value是共享了一个PRESENT(是Object类型的常量对象)

JDK1.8:HashMap的底层实现是:数组+链表/红黑树
 1、复杂回答v1.0
   (1)映射关系的类型
         添加到HashMap1.8种的映射关系的对象是HashMap.Node类型,或者是HashMap.TreeNode类型。
         它也是Map.Entry接口的实现类。
   (2)映射关系添加到table[i]中时,如果里面是链表,新的映射关系是作为原来链表的尾部
          “七上八下”:JDK1.7在上,JDK1.8在下
          为什么要在下面,因为如果是红黑树,那么是在叶子上,保持一致,都在下面。
  (3)扩容的时机不同
        第一种扩容:元素个数size达到阈值threshod = table.length * 加载因子  并且table[i]是非空的
        第二种扩容:当某个桶table[index]下面的结点的总数原来已经达到了8个,这个时候,要添加第9个时,会检查
        table.length是否达到64,如果没达到就扩容。如果添加第10个时,也会检查table.length是否达到64,如果没达到就扩容。
        为什么,因为一旦扩容,所有的映射关系要重新计算hash和index,一计算原来table[index]下面可能就没有8个,或新的映射关系也可能不在table[index],
        这样就可能均匀分布。
 (4)什么时候从链表变成红黑树
        当table[index]下面结点的总数已经达到8个,并且table.length也已经达到64,那么再把映射关系添加到
        table[index]下时,就会把原来的链表修改红黑树
 (5)什么时候会从红黑树变为链表
        当删除映射关系时table[index]下面结点的总数少于6个,会把table[index]下面的红黑树变回链表。

2、put的源代码v2.0
   第一步:计算key的hash,用了一个hash()函数,目的得到一个比hashCode更合理分布的一个int值
   第二步:如果table是空的,那么把table初始化为长度为16的数组,阈值初始化为= 默认的长度16 * 默认的加载因子0.75 = 12
   DEFAULT_INITIAL_CAPACITY:默认的初始化容量16
   DEFAULT_LOAD_FACTOR:默认加载因子 0.75
   第三步:查看table[index]是否为空,如果为空,就直接new一个Node放进去
    index = hash的int值 & table.length-1
   第四步:先查看table[index]的根节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
   第五步:如果table[index]的根节点的key与新添加的映射关系的key不同,
   还要看table[index]根结点的类型是Node还是TreeNode类型,
 

   如果是Node类型,那么就查看链表下的所有节点是否有key与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
   如果是TreeNode类型,那么就查看红黑树下的所有叶子节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
  第六步:如果没有找到table[index]有结点与新添加的映射关系的key“相同”,那么
   如果是TreeNode类型,那么新的映射关系就创建为一个TreeNode,连接到红黑树中
   如果是Node类型,那么要查看链表的结点的个数,是否达到8个,如果8个,并且table.length小于64,那么先扩容。
   TREEIFY_THRESHOLD:树化阈值8
   MIN_TREEIFY_CAPACITY:最小树化容量64
   UNTREEIFY_THRESHOLD:反树化,从数变为链表的阈值6

泛型:Generic,JDK1.5引入
 1、为什么要泛型?
   (1)没有泛型很麻烦,总要强制类型转换
   (2)没有泛型不安全
 2、泛型是什么?
   生活中比喻:泛型好比是瓶子等容器上的商标,或者中药店抽屉上的标签。
   集合中的泛型:设计集合之初,是不知道将来用来装什么对象,如果使用者想要表明这个集合用来装什么,就可以贴标签。
   实际上泛型是什么呢?
   泛型是指参数化的类型
   现在设计集合等类型时,不知道其中的元素等的类型,那么把这个类型设计为参数,让使用者决定
   class ArrayList<E>:E就是泛型形参
   ArrayList<String>:这个String就泛型实参
  为了区别:
    形参:数据形参和类型形参
       数据形参:方法签名中,声明方法时getMax(int a, int b)
      泛型形参:在类或接口名后面   ArrayList<E>,Comparable<T>
                        在方法签名中,声明方法时,在返回值类型的前面<T>
                  public static <T> boolean addAll(Collection<? super T> c,T... elements)
   实参:数据实参和类型实参
      数据实参:调用方法是 getMax(5,6)
      泛型实参:ArrayList<String>

posted @ 2019-10-09 18:16  zzz222zzz  阅读(464)  评论(0编辑  收藏  举报