一、Collection接口

java 清空int数组内容 java如何清空数组中的数据_数组

List是有序的队列,List中可以有重复的元素

Set是数学概念中的集合,Set中没有重复元素

二 、list集合源码解析

1、 Arraylist

数据结构:

java 清空int数组内容 java如何清空数组中的数据_java_02

特点:

  1. arrayList可以存放null。
  2. arrayList本质上就是一个elementData数组(默认值为10的数组)。
  3. arrayList区别于数组的地方在于能够自动扩展大小,其中关键的方法就是gorw()方法。
  4. arrayList中removeAll(collection c)和clear()的区别就是removeAll可以删除批量指定的元素,而clear是全是删除集合中的元素。
  5. arrayList由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多,有移动很多数据才能达到应有的效果
  6. arrayList实现了RandomAccess,所以在遍历它的时候推荐使用for循环。
  7. 新的容量 = 原始容量 * 1.5 --> int newCapacity = oldCapacity + (oldCapacity >> 1), --扩容代码
    int newCapacity = oldCapacity + oldCapacity / 2

2、 LinkList

数据结构:(双向链表)

java 清空int数组内容 java如何清空数组中的数据_java_03

特点:

  1. linkedList本质上是一个双向链表,通过一个Node内部类实现的这种链表结构。
  2. 能存储null值
  3. 跟arrayList相比较,就真正的知道了,LinkedList在删除和增加等操作上性能好,而ArrayList在查询的性能上好
  4. 从源码中看,它不存在容量不足的情况(不需要指定元素个数,能链多长就链多长)
  5. linkedList不光能够向前迭代,还能像后迭代,并且在迭代的过程中,可以修改值、添加值、还能移除值。
  6. linkedList不光能当链表,还能当队列使用,这个就是因为实现了Deque接口。

3、Vector

  1. 矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现
  2. 但是ArrayList是非线程安全的,而Vector是线程安全的。

4. Stack

  1. 栈,它继承于Vector
  2. 它的特性是:先进后出(FILO, First In Last Out)。

5.List总结

a.各自的使用场景:

1. 对于需要快速插入,删除元素,应该使用LinkedList

2. 对于需要快速随机访问元素,应该使用ArrayList

3. 对于“单线程环境 ,List仅仅只会被单个线程操作",此时应该使用非同步的类(如ArrayList、LinkedList)。

4.对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector、

CopyOnWriteArrayList)。现在来说使用**CopyOnWriteArrayList**比较多

b.问答

1.为什么LinkedList中插入元素很快,而ArrayList中插入元素很慢!(“删除元素”与“插入元素”的原理类似

LinkedList:	通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置	
	index;找到之后,再插入一个新节点。双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度	的1/2,则从前向后查找; 否则,从后向前查找。
	(每次指定位置添加元素,都会判断位置的前后,从而减少查询的时间)

ArrayList: System.arraycopy(elementData, index, elementData, index + 1, size - index); 会移动index之
	后所有元素即可。这就意味着,ArrayList的add(int index, E element)函数,会引起index之后所有元素的改变!
	(每次指定位置添加元素,指定位置后的数据都要被copy一下)

2. 为什么LinkedList中随机访问很慢,而ArrayList中随机访问很快

LinkedList:通过get(int index)获取LinkedList第index个元素时。先是在双向链表中找到要index位置的元素;找到之后
	再返回。双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否
	则,从后向前查找。
	
ArrayList:通过get(int index)获取ArrayList第index个元素时。直接返回数组中index位置的元素,而不需要像 	 	LinkedList一样进行查找。
	
主要原因:ArrayList实现了RandmoAccess接口,提供了随机访问功能,而LinkedList不能随机访问,只能一个个遍历过去

三 、map集合源码解析

1. HashMap

(推荐看)

(详细介绍)

https://www.hollischuang.com/archives/2431 (关于初始值问题,默认为16)

(介绍 “(n - 1) & hash”,就是通过它来获取指定key在数组中的位置)

数据结构:(数组加链表加红黑树)

java 清空int数组内容 java如何清空数组中的数据_java 清空int数组内容_04

hashMap中的常量:

threshold = capacity * loadFactor    --阈值当Size>=threshold的时候,那么就要考虑对数组的扩增了,也就是说,这个的意思就是衡量数组是否需要扩增的一个标准。
 loadFactor     -- 填充因子,一般我们设置默认值为0.75
 size/capacity  -- 计算HashMap的实时装载因子的方法为,而不是占用桶的数量去除以capacity
 capacity       -- 数组长度
 size           -- hashmap中数据的个数(链表中的也算)

特点:

1. 数据结构:在JDK1.8以前是一个链表散列这样一个数据结构,而在JDK1.8以后是一个数组加链表加红黑树的数据结构。

2. hashMap是一个能快速通过key获取到value值的一个集合

问答

  1. hashMap它是何时、如何扩容的?
当插入一个元素(put())的时候,size加1,若size大于threshold的时候,就会进行扩容(resize():每次扩容2的次幂)。扩容会伴随着一次重新hash分配,并且会遍历hash表中所有的元素,是非常耗时的。在编写程序中,要尽量避免它调用resize()方法。
  注意:当所有元素都在同一个桶里面,并且这个桶的长度超过了threshold,它也会进行扩容
  1. hashMap初始值应该怎么设置?
***注意:底层数组长度会一直保持2的次幂,例如初始是16,之后自动扩容为32、64...,
          并且,初始值设为3,但是hashMao它会自动帮我们扩展为4,反正无论如何,都是2的次幂***
   
   为了最大程度的避免扩容带来的性能消耗,我们建议可以把默认容量的数字设置成expectedSize / 0.75F + 1.0F 。在日常开发中,可以使用
  1. hashMap是如何储存数据的?
数组加链表加红黑树,数组里面存链表,当链表数》=8时,他就会自动变成红黑树。在这里,我们把数组的一个位置,称为一个桶,桶里面装的就是链表或红黑树。
  1. hashMap它查找数据为什么那么快?
原因是内部使用hash查找值的方法,能很快找到了我们指定Key存放的桶。之后我们再遍历桶找到我们需要的key-value。(例如100个元素平均装在10个桶里面,所以我们通过hash值找到了指定的桶之后,只要最大遍历10次就能找到需要的元素了。当然,根据  threshold = capacity * loadFactor = 10*0.75=75<100,明显超出了过了,早就得扩容了)
  1. 算出元素在数组中的位置索引: “(n - 1) & hash”,它是怎么计算的?
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);//通过与运算,将h的二进制,和length-1的二进制进行与运算得出的结果就是数组中的位置。
}
	上面是源码,h是key的hash值,length是数组的长度。
	当 array.ength长度是2的次幂时,key.hashcode % array.length == key.hashcode & (array.length - 1)
,相当于进行了取余操作!

   为什么要这样做?
	1.  & 操作代替 % 操作,提升性能
	2.  数组扩容时,仅仅关注 “特殊位” 就可以重新定位元素
	3.  如何实现建议看上面的链接文章

2. LinkedHashMap

数据结构:(数组加双向链表加红黑树)

java 清空int数组内容 java如何清空数组中的数据_集合_05

特点:

  1. 在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。

四、fail-fast原理

(主要看评论)

原因:每次我们队集合进行增删改操作的时候,都会先运行下面这个函数,若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。

// 用来记录List修改的次数:每修改一次(添加/删除等操作),将modCount+1(它是在AbstractList中定义的)
protected transient int modCount = 0;


int expectedModCount = modCount;         //这行代码在迭代器中定义的,所以我们使用迭代器的时候就已经获取了modCount的值,所以在迭代过程中,只要进行了增删改之类的操作,就会运行下面这个方法,进而报错

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

解决:使用 CopyOnWriteArrayList 就不会报错了

释: CopyOnWriteArrayList 直接继承了List接口,是自己实现Iterator。

原理:CopyOnWriteArrayList的add、set、remove等会改变原数组的方法中,都是先copy一份原来的array,再在copy数组上进行add、set、remove操作,这就才不影响COWIterator那份数组

问答:

1. 在CopyOnWriteArrayList的迭代器中没有比较两个modCount,如果在ArrayList中不比较两个modCount也不会爆那个异常?   	 所以报不报异常就看有没有比较modCount2. 因为这个异常是比较不一致以后人为主动扔出来的首先我们要知道ArrayList比较的目的是什么?   	保证数据一致性啊3. 现在的问题是为什么CopyOnWriteArrayList可以不比较modCount也能保证数据一致性?   	因为getArray()返回的array的类型是volatile的(强制内存一致性)
  1. 在CopyOnWriteArrayList的迭代器中没有比较两个modCount,如果在ArrayList中不比较两个modCount也不会爆那个异常。
    所以报不报异常就看有没有比较modCount
  2. 因为这个异常是比较不一致以后人为主动扔出来的首先我们要知道ArrayList比较的目的是什么?
    保证数据一致性啊
  3. 现在的问题是为什么CopyOnWriteArrayList可以不比较modCount也能保证数据一致性?
    因为getArray()返回的array的类型是volatile的(强制内存一致性)