集合

|--- Collection:单列集合,用来存储一个一个的对象
|--- List:有序、可重复
|--- ArrayList 主要实现类,线程不安全,效率高,使用Object[] elementData存储
|--- LinkedList 频繁插入、删除操作,效率比ArrayList高,使用双向链表存储
|--- Vector 古老实现类,线程安全,效率低,使用Object[] elementData存储
|--- Set:无序、不可重复
|--- HashSet 主要实现类,线程不安全,可以存储null值
|--- LinkedHashSet 作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历

  •             				在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
                    							对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
                    	|--- TreeSet 可以按照添加对象的指定属性,进行排序
    

遍历方式:Iterator 和 for-each循环方式(jdk5.0新增)
迭代器实现代码:
Iterator iterator = coll.iterator();//获取一个迭代器实例
while(iterator.hasNext()){//判断是否还有下一个元素
sout(iterator.next());//指针下移,将下移以后集合位置上的元素返回
}

List接口:添加的对象,所在的类要重写equals()方法
常用方法:add(Object obj)、remove(int index)/remove(Object obj)
set(int index,Object ele)、get(int index)
add(int index,Object ele)、size()
ArrayList源码分析:(jdk7)
ArrayList list = new ArrayList();//底层创建长度为10的Object[] elementData
list.add(123);//elementData[0] = new Integer(123);
...
list.add(11);//容量不够,扩容为原来的1.5倍,同时将原有数组的数据复制到新的数组中
结论:建议使用带参的构造器:ArrayList list = new ArrayList(int capacity);

ArrayList源码分析:(jdk8)
	ArrayList list = new ArrayList();//底层创建Object[] elementData,初始化{}
	list.add(123);//第一次调用add时,底层才创建长度为10的数组,其余和jdk7一致
结论:延迟了数组的创建,节省内存

LinkedList源码分析:
	LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
	list.add(123);//将123封装到Node中,创建了Node对象。
	其中,Node定义为:体现了LinkedList的双向链表的说法
      private static class Node<E> {
			E item;
			Node<E> next;
			Node<E> prev;

			Node(Node<E> prev, E element, Node<E> next) {
			this.item = element;
			this.next = next;
			this.prev = prev;
			}
		}
	
Vector源码分析:
	jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
	在扩容方面,默认扩容为原来的数组长度的2倍。

Set接口:(以HashSet为例)
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置,判断
数组此位置上是否已经元素:
如果此位置上没其他元素,则元素a添加成功。 --->情况1
如果此位置上其他元素b(或以链表形式存在的多个元素,则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。--->情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。--->情况2
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a

Map接口:
|----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)

  •   |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
    
  •   |----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
    
  •   原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
    
  •   对于频繁的遍历操作,此类执行效率高于HashMap。
    
  •   |----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
    
  •   底层使用红黑树
    
  •   |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
    
  •   |----Properties:常用来处理配置文件。key和value都是String类型
    
  •   HashMap的底层:数组+链表  (jdk7及之前)
    
  •   数组+链表+红黑树 (jdk 8)
    

Map中的key:无序的、不可重复的,使用Set存储所的key ---> key所在的类要重写equals()和hashCode() (以HashMap为例)
Map中的value:无序的、可重复的,使用Collection存储所的value --->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序的、不可重复的,使用Set存储所的entry

  1. 常用方法
  • 添加:put(Object key,Object value)
  • 删除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查询:get(Object key)
  • 长度:size()
  • 遍历:keySet() / values() / entrySet()
  1. 内存结构说明:(难点)
    4.1 HashMap在jdk7中实现原理:
    HashMap map = new HashMap():

    •  在实例化以后,底层创建了长度是16的一维数组Entry[] table。
      
    •  ...可能已经执行过多次put...
      
    •  map.put(key1,value1):
      
    •  首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
      
    •  如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
      
    •  如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
      
    •  如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
      
    •  如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
      
    •  如果equals()返回false:此时key1-value1添加成功。----情况3
      
    •  如果equals()返回true:使用value1替换value2。
       *
      
    •  补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
       *
      
    •  在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原的数据复制过来。
      

    4.2 HashMap在jdk8中相较于jdk7在底层实现方面的不同:

    1. new HashMap():底层没创建一个长度为16的数组
    2. jdk 8底层的数组是:Node[],而非Entry[]
    3. 首次调用put()方法时,底层创建长度为16的数组
    4. jdk7底层结构只:数组+链表。jdk8中底层结构:数组+链表+红黑树。
      4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
      4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储
      4.3 HashMap底层典型属性的属性的说明:
      DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
      DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
      threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
      TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
      MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

说明:ArrayList和HashMap都是线程不安全的,如果程序要求线程安全,我们可以将ArrayList、HashMap转换为线程的。
使用synchronizedList(List list) 和 synchronizedMap(Map map)

posted @ 2021-10-09 14:15  LinAlex  阅读(24)  评论(0)    收藏  举报