集合知识点

集合
集合关系

1、Iterator
Iterator 迭代器接口,集合继承了这个接口
有三个方法: hasNext() next() remove()

当迭代遍历集合同时删除时:
使用 Iterator 的remove() 而不是Collection的remove()

若使用collection的remove()会出现迭代器并发修改异常
ConcurrentModificationException

ListIterator继承Iterator接口,只能用于List集合遍历。
比Iterator功能更加强大:
ListIterator和Iterator区别:

1、iterator()方法在Set和List接口中都有定义,但是listIterator()仅存在于list接口中(或实现类中);
2、ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator可以通过hasPrevious()和previous()方法实现顺序向前移动;
3、iterator()方法和listIterator()方法都可以产生一个指向List集合开始出的Iterator对象,但是listIterator(n)方法可以创建一个一开始就指向列表索引为n的元素出的ListIterator;
4、ListIterator有add()方法,可以向List中添加对象,而Iterator不能;
5、ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能;
6、ListIterator可以使用set()方法实现对List集合中元素的修改。Iterator仅能遍历,不能修改。

可修改set(),可新增add() ,向前迭代hasPrevious()和previous()

集合遍历方式:
1、collection继承了iterator 所以List和 set可以使用迭代器遍历
Map没有继承Iterator不能直接使用迭代器遍历。但是可以使用map.keyset()获取键值集合,然后迭代
2、List 普通for ,增强for 迭代器
Set 增强for 迭代器
Map 迭代器

2、Collection

Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 ,没有同步, 线程不安全-
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMap
.List和Set总结:
(1)List,Set都是继承自Collection接口,Map则不是
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
(3).Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
(4)、ArrayList与LinkedList的区别和适用场景
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。

HashSet原理:
继承set,无序性,唯一性。底层为哈希表结构,基于hashMap实现
原理:
①是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
②当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
③HashSet的其他操作都是基于HashMap的。

LinkHashSet 和 HashSet :
LinkHashSet 继承HashSet,底层实现 双向链表+hash表 保证了继承对象的存取顺序相同。

TreeSet:
有序,唯一的Set,基于TreeMap 而TreeMap又基于红黑二叉树数据结构实现
原理:
1、默认创建一个TreeMap .集合的所有元素作为Map的key. 定义一个Final object作为value
2、TreeSet 方法内部调用TreeMap的方法。需要了解TreeSet
保证TreeMap key值唯一性的方法:
1 、key 元素(TreeSet集内元素)必须实现Comparable 接口,(针对自定义元素)
重写方法:
2、TreeMap/TreeSet 构造方传入CompareTor接口的实现类。
重写方法:

3、Map
1、基于key /value 键值对存储 映射关系

1、常见方法
get(key ) ,put(key,value), keySet(), entrySet()

2、HashMap
JDK1.8以前 数组+单向链表
JDK1.8以后 数组+单向链表+红黑树

基于哈希表结构实现,哈希表=数组+链表
数组数据: Entry<>[]= new Arry[];

1.8之后的put()方法:

put:(key-value)方法是HashMap中最重要的方法,使用HashMap最主要使用的就是put,get两个方法。
1.判断键值对数组table[i]是否为空或者为null,否则执行resize()进行扩容;
2.根据键值key计算hash值得到插入的数组索引  i  ,如果table[i] == null ,直接新建节点添加即可,转入6,如果table[i] 不为空,则转向3;
3.判断table[i] 的首个元素是否和key一样,如果相同(hashCode和equals)直接覆盖value,否则转向4;
4.判断table[i] 是否为treeNode,即table[i]是否为红黑树,如果是红黑树,则直接插入键值对,否则转向5;
5.遍历table[i] ,判断链表长度是否大于8,大于8的话把链表转换成红黑树,进行插入操作,否则进行链表插入操作;便利时遇到相同key直接覆盖value;
6.插入成功后,判断实际存在的键值对数量size是否超过了threshold,如果超过,则扩容;
  
Entry内部类。链表的实现方式:

HashMap里面实现一个静态内部类Entry,其重要的属性有 hash,key,value,next。
HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。到这里为止,HashMap的大致实现,我们应该已经清楚了。

避免哈希冲突:
1、二次哈希计算:hash(key.hashCode())%len 散列均匀
2、链表解决哈希冲突

Jdk1.8HashMap新特性:
1、1.8之前数据结构是数组+链表,1.8之后是数组+链表+红黑树
当链表的长度大于7时,将链表转化成红黑树。避免链表过长导致的性能降低
2、1.8之前是头插法,1.8之后是尾插法
3、jdk1.7中当哈希表为空时,会先调用inflateTable()初始化一个数组;而1.8则是直接调用resize()扩容;

3、LinkHashMap
LinkHashMap集成HashMap 实现了存储顺序相同,
原理:
哈希表+双向链表。链表存储着上下对象的地址指针

4、CurrentHashMap
hashMap是线程不安全的Map,如何保证线程安全

方式:

  1. hashtable
    HashTable和HashMap数据结构一致 :数组+链表
    synchronized来保证线程安全的,所有线程竞争同一把锁。锁对象是Entry<>[] 数组。使用阻塞同步,效率低。

  2. collections.synchronizedMap(map)
    也是采用synchronized方法上加锁,使用阻塞同步,效率低。
    3、CurrentHashMap

CurrentHashMap数据结构:
ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。

Jdk1.8之后的结构:
Jdk1.8之后使用和hashMap相同的结构:数组+链表+红黑树 。同时加上大量的Cas操作。
CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,如果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

实现原理:
Jdk1.8之后CurrentHashMap 锁对象由原来的Segment,变成数组的每个元素Node。实现的锁的粒度细化,更加适用于高并发场景,避免线程堵塞。

5、TreeMap
继承Map,实现有序,key唯一的键值对集合,基于红黑树实现key唯一和排序
 红黑树:
 红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性。同时红黑树更是一颗自平衡的排序二叉树。
 
平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。也就是说该二叉树的任何一个等等子节点,其左右子树的高度都相近。

红黑树顾名思义就是节点是红色或者黑色的平衡二叉树,它通过颜色的约束来维持着二叉树的平衡。对于一棵有效的红黑树二叉树而言我们必须增加如下规则:
       1、每个节点都只能是红色或者黑色
       2、根节点是黑色
       3、每个叶节点(NIL节点,空节点)是黑色的。
       4、如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
       5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

TreeMap实现方式:
1、通过key元素必须实现compareAble接口,重写comareTo()方法自定义排序规则。
2、key元素没有实现compareAble接口,但是TreeMap创建实例时必须在构造方法中传入CompareTor接口的实现类,自定义排序规则

6、Map 排序和遍历
Map按照key排序输出:
Map如果按照key值自然排序或自定义排序。
1、将Map转化成TreeMap
2、key元素必须实现接口CompareAble 或者TreeMap的构造方法传入CompareTor接口实现类
3、在实现接口CompareAble 或是CompareTor 后重写方法compareTo();该方法可实现自定义排序

Map按照Value排序输出:
使用collections.sort()方法;
步骤:
1、将Map内部类转化成List
List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
2、使用collections.sort()方法,传入自定义CompareTor接口实现类,实现自定义排序规则设置;
Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
public int compare(Entry<String, String> o1, //升序排序
Entry<String, String> o2) {
return o1.getValue().compareTo(o2.getValue());
}});
使用JDK1.8新特性集合转化成流排序
根据key排序
Map<String,String> result = new HashMap<>();

Map<String,String> map = new HashMap<>();

map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEachOrdered(x->result.put(x.getKey(),x.getValue()));

根据Value排序
Map<String, Integer> valueResult = new HashMap<>();
Map<String, Integer> map = new HashMap<>();
map.entrySet().stream()
.sorted(Map.Entry
.comparingByValue())
.forEachOrdered(b->valueResult.put(b.getKey(), b.getValue()));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值