集合
List集合
- ArrayList 实现一个数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步,它是以Array方式实现的List,允许快速随机存取。
- LinkedList实现一个链表,提供最佳顺序存取,适合插入和移除元素。由这个类定义的链表也可以像栈或队列一样被使用。提供最佳顺序存取,适合插入和移除元素。
Set集合
Set也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。
- HashSet 能够快速定位一个元素,要注意的是:存入HashSet中的对象必须实现HashCode()方法
- TreeSet 将放入其中的元素按序存放。
Map集合
映射与集或列表有明显区别,映射中每个项都是成对的,Map是把键对象和值对象进行关联的容器。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的,也就是说Map中的键对象不允许重复,这是为了保证查询结果的一致性。
- HashMap 实现一个键到值映射的哈希表,通过键取得值对象,没有顺序,通过get(key)来获取value,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个);
- HashTable 实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
- 当元素的顺序很重要时选用TreeMap,当元素不必以特定的顺序进行存储时,使用HashMap。Hashtable的使用不被推荐,因为HashMap提供了所有类似的功能,并且速度更快。当你需要在多线程环境下使用时,HashMap也可以转换为同步的。
Iterator迭代器
Iterator接口位于java.util包中,它是一个对集合进行迭代的迭代器。
集合容器(如:List、Set、Map等)本身提供了处理元素置入和取出的方式,但是单一选取元素的方法很受限制。所以我们要用Iterator去选取容器中的元素,它将容器转换成一个序列。
一些集合类提供了内容遍历的功能,通过java.util.Iterator接口。这些接口允许遍历对象的集合。依次操作每个元素对象。当使用 Iterators时,在获得Iterator的时候包含一个集合快照。通常在遍历一个Iterator的时候不建议修改集合本省。
//迭代器的使用 配合泛型
Iterator<Person> iterator = list2.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
各个集合的底部实现原理
-
ArrayList实现原理要点概括
ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素。
底层使用数组实现
该集合是可变长度数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量增长大约是其容量的1.5倍,这种操作的代价很高。
采用了Fail-Fast机制,面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险-
LinkedList实现原理要点概括
参考文献
LinkedList是链式存储的线性表,实质是双向链表,实现了List和Deque接口。Deque代表双端队列,既可以当做队列也可以当作栈。
LinkedList是List接口的双向链表非同步实现,并允许包括null在内的所有元素。
底层的数据结构是基于双向链表的,该数据结构我们称为节点
双向链表节点对应的类Entry的实例,Entry中包含成员变量:previous,next,element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。 -
HashMap实现原理要点概括
HashMap是基于哈希表的Map接口的非同步实现,允许使用null值和null键,但不保证映射的顺序。
底层使用数组实现,数组中每一项是个链表,即数组和链表的结合体
HashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据key的hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据key的hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
HashMap进行数组扩容需要重新计算扩容后每个元素在数组中的位置,很耗性能
采用了Fail-Fast机制,通过一个modCount值记录修改次数,对HashMap内容的修改都将增加这个值。迭代器初始化过程中会将这个值赋给迭代器的expectedModCount,在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map,马上抛出异常HashMap有两个参数影响其性能:初始容量和加载因子。默认初始容量是16,加载因子是0.75。容量是哈希表中桶(Entry数组)的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。
也就是说最多能放16*0.75=12个元素,当put第13个时,HashMap将发生rehash,rehash的一系列处理比较影响性能,所以当我们需要向HashMap存放较多元素时,最好指定合适的初始容量和加载因子,否则HashMap默认只能存12个元素,将会发生多次rehash操作。无论你的HashMap(x)中的x设置为多少,HashMap的大小都是2n。2n是大于x的第一个数。 如果x=100,那么HashMap的初始大小应该是128.但是100/128=0.78,已经超过默认加载因子的大小了。因此会resize一次,变成256。所以最好的结果还是256。
hash生成算法可能产生:哈希碰撞ddos漏洞,使得系统崩溃
-
Hashtable实现原理要点概括
Hashtable是基于哈希表的Map接口的同步实现,不允许使用null值和null键
底层使用数组实现,数组中每一项是个单链表,即数组和链表的结合体
Hashtable在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。Hashtable底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据key的hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据key的hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。 -
ConcurrentHashMap实现原理要点概括
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。
它使用了多个锁来控制对hash表的不同段进行的修改,每个段其实就是一个小的hashtable,它们有自己的锁。只要多个并发发生在不同的段上,它们就可以并发进行。
ConcurrentHashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。Hashtable底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据key的hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据key的hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
与HashMap不同的是,ConcurrentHashMap使用多个子Hash表,也就是段(Segment)
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。 -
HashSet实现原理要点概括
HashSet由哈希表(实际上是一个HashMap实例)支持,不保证set的迭代顺序,并允许使用null元素。
基于HashMap实现,API也是对HashMap的行为进行了封装,可参考HashMap -
LinkedHashMap实现原理要点概括
LinkedHashMap继承于HashMap,底层使用哈希表和双向链表来保存所有元素,并且它是非同步,允许使用null值和null键。
基本操作与父类HashMap相似,通过重写HashMap相关方法,重新定义了数组中保存的元素Entry,来实现自己的链接列表特性。该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而构成了双向链接列表。 -
LinkedHashSet实现原理要点概括
对于LinkedHashSet而言,它继承与HashSet、又基于LinkedHashMap来实现的。LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同。
-
HashMap和HashSet原理
HashMap底层是用数组链表存储的,元素是Entry。向HashMap添加<Key,Value>时,由key的hashcode决定Entry存储位置,当两个Entry对象的key的hashcode相同时,由key的equals()方法返回值决定采用覆盖行为(返回true),还是在链表头添加新的Entry(返回false)。Collection values(),返回集合对象,但不能添加元素,主要是用来遍历。自定义类如果放入HashMap或HashSet中,需要重写equals和hashcode方法。 -
TreeMap和TreeSet原理
TreeMap底层是用红黑树来存储,每个Entry对应树的一个节点,TreeMap元素默认从小到大排序。V put(Key k, Value v)实质是二叉排序树的插入算法
-