java集合概述

java容器
    集合容器概述
        什么是集合
            用于存储数据的容器
        集合的特点
            对象封装数据,对象多了也需要存储,集合用于存储对象
            对象的个数可以确定使用数组,对象的个数不确定可以使用集合,因为集合的长度是可变的
        集合和数组的区别
            数组长度固定;集合的长度可变
            数组可以存储基本数据类型和引用数据类型;集合只能存储引用数据类型
            数组存储的元素必须是相同的数据类型;集合存储的对象可以是不同的数据类型
        使用集合框架的好处
            容量自增长、灵活
            提供了高性能的数据结构和算法,使编码更简单,提高程序的速度和质量
            允许不同的api相互操作、api之间可以来回传递集合
        常用的集合类有哪些及集合框架底层数据结构
            collection
                Set
                    hashSet
                        基于hashMap实现,底层采用hashMap保存元素(不可重复无序)
                    treeSet
                        红黑树(自平衡的排序二叉树)    不可重复、有序
                    linkedHashSet
                        继承于hashSet,内部是通过linkedHashMap实现的
                List
                    ArrayList
                        Object数组
                    LinkedList
                        双向循环链表
                    stack
                    vector
                        Object数组
            Map
                HashMap
                    jdk1.8之前是数组+链表【数组是hashMap的主体,链表则是为了解决hash冲突,使用《拉链法》解决】;jdk1.8之后在解决hash冲突时有了较大的变化,当链表长度大于阈值(默认是8)时,将链表转为红黑树,以减少搜索的时间,小于阈值会在退为链表
                treeMap
                    红黑树(自平衡的排序二叉树)
                hashTable
                    数组+链表
                concurrentHashMap
                    子主题 1
                propertes
                linkedHashMap
                    继承与hashMap,在其基础上增加了一个双向链表,使其上面的结构可以保持插入时的顺序,同时通过对链表进行相对应的操作,实现了访问顺序相关上的逻辑
        List、Set、Map区别,List、Set、Map是否都继承与Collection接口,三者在存取元素时有什么区别
            List
                一个有序的容器、元素可以重复、可以插入多个null,通过索引获取
            Set
                一个无序的容器、不允许存在重复的元素、只能存一个null
            Map
                是一个键值对集合,存储键、值之间的映射。key唯一,value不要求有序可重复。
        哪些集合是线程安全的
            vector
            stack
            hashTable
            enumeration(枚举)
        快速失败机制,fail-fast
            java集合的一种错误检测机制,当多个线程对集合进行结构上的改变时,有可能会产生;如在循环时remove();
            解决办法
                1.在遍历过程中,所有涉及到改变modCount值得地方全部加上同步锁(synchronized)
                2.使用copyOnWriteArrayList替换ArrayList
        创建一个只读的集合
            Collection.unmodifiableCollection(Collection coll) 方法创建一个只读的集合
    Collection接口
        List
            Iterator 是什么
                【迭代器】提供了任何遍历Collection的接口
            iterator怎么使用?有什么特点?
                使用
                    Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
        }
                特点
                    只能单向遍历,但是更加安全,他可以确保在遍历集合元素被更改时会抛出异常
            如何边遍历边移除Collection中的对象
                使用iterator.remove()
            iterator和ListIterator区别
                iterator可以遍历list和set;而ListIterator只能遍历list
                iterator单向遍历,ListIterator可以双向遍历(向前、向后遍历)
                ListIterator实现了iterator接口,然后添加了额外的功能,如:添加一个元素、替换一个元素、获取前面、后面的元素索引位置
            遍历一个List有几种方法,最佳实践是什么?
                collection中提供了一个RandomAccess接口用来标记List是否支持randomAccess;支持的列表可以用for循环遍历,不支持的简历使用iterator或者foreach遍历
            说一下ArrayList的优缺点
                优点
                    底层以数组实现,是一种随机访问模式,实现了randomAccess接口,查询效率高
                    在顺序添加一个元素时非常方便
                    查询效率高
                缺点
                    删除元素时需要一次元素复制操作,如果复制的元素很多会比较耗时
                    插入时也一样
                    新增删除效率低
            如何实现数组和集合的相互转换
                数组转集合
                    Arrays.asList()
                集合转数组
                    .toArray()
            ArrayList和LinkedList区别
                数据结构实现
                    arrayList是数组,linkedList是双向链表
                随机访问效率
                    ArrayList比linkedList随机访问效率要高,因为linkedList是线性数据存储方式,需要移动指针从前往后一次查找
                增加和删除效率
                    linkedlist效率比arrayList效率高
                内存空间占用
                    linkedList比较占用空间,因为他还存着前后两个元素的引用
            ArrayList和Vector区别
                线程安全
                    vector是线程安全的
                性能
                    ArrayList比vector高
                扩容
                    vector每次扩容增加一倍,ArrayList只会增加50%
            插入数据时ArrayList、LinkedList、vector谁速度更快?阐述下他们之间的存储性能和特性
                底层都是数组的方式存储数据,vector是线程安全的,他内部的所有方法都加上了同步锁,但是性能比arrayList差,linkedList使用双向链表实现存储,查询需要一个一个遍历,但是插入只需记录当前项前后引用即可,所以插入linkedList快
            多线程场景下怎么使用arrayList
                使用Collection.synchronizedList()方法将其转换成线程安全的容器后使用
            为什么ArrayList的elementData上加上了transient修饰
                arrayList实现了序列化接口,transient的作用是不希望elementData数据被序列化,因为每次序列化都会先调用defaultWirteObject()方法序列化ArrayList中的非transient元素,然后遍历elementData,只序列化已存入的元素,这样加快了序列化的速度也减少了序列化之后的文件大小
            List和Set的区别
                一个有序一个无序,一个可重复一个不可重复,set检索元素效率低,删除和插入效率高,删除和插入不会引起元素位置的改变;List查询效率高,删除和插入效率低,会影响其他元素位置的改变.
        Set
            说一下hashSet的实现原理
            hashSet怎么检查重复及如何保证数据不可重复
                add()时会比较hash值,同事还会结合equles方法比较,
                hashSet的add是hashMap的put
                hashMap的key是唯一的,hashSet加入的值就是hashMap的key,并且在相同时会用新的key覆盖旧的key然后返回旧的key。所以不会重复(先比较hash在比较equals)
            hashCode和equals
                如果两个对象相等,hashCode也是相同
                hashCode相同,两个对象不一定相同
                如果两个对象相同equals也是返回true
                如果equals被覆盖那个hashCode方法也必须被覆盖
                hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashcode(),则改class的两个对象无论如何都不会相等
            ==与equals的区别
                ==是判断两个变量或者实例是不是指向同一个内存空间;equals比较的是所指向内存空间的值是不是相同
                ==是引用是否相同;equals()是指值是否相同
        Queue
            blockingQueue是什么?
                阻塞队列
            在queue中poll()和remove()有什么区别
                相同点
                    都是返回第一个元素并在队列中删除返回的对象
                不同点
                    如果没有元素poll()会返回null;remove会抛出异常
    Map接口
        HashMap实现原理
            概述
                HashMap是基于哈希表的Map接口的非同步实现。提供所有可选的映射操作,并允许使用null值和null键,不不保证映射的顺序
            数据结构
                数组+链表+红黑树
            基于hash算法实现的
                1.put时,利用key的hashCode重新计算hash计算当前对象元素在数组中的下标
                2.存储时,如果hash值相同的key,此时有两种情况:1)如果key相同则覆盖原始值;2)如果key不同(哈希冲突),则将当前的key-value放入链表中
                3.获取时,直接找到hash值对应的下标,再进一步判断key是否相同 从而拿到对应值
                解决hash冲突的核心就在于:使用数组的存储方式,将冲突的key的对象放入到链表中,发生冲突就在链表中寻找
                jdk1.8中当链表中的节点超过8时,改链表会转换为红黑树,当小于6时会回退为链表
        jdk1.7和jdk1.8HashMap区别
            概述
                在java中,保存数据有两种比较简单的数据结构:数组和链表。
                    数组:寻址容易,但是插入和删除困难
                    链表:寻址困难,但是插入和删除容易
                    将数组和链表的结合在一起,发挥两者各自的优势,使用拉链法解决哈希冲突
            jdk1.8之前
                解决哈希冲突:拉链法:将链表和数组结合,创建一个链表数组,数组中的每一格就是一个链表,若遇到哈希冲突,将冲突的值加到链表中
                    如果链表长度太长影响查询效率
            jdk1.8之后
                解决哈希冲突:对拉链法进行了优化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,提高查询效率
                优化点
                    1.resize扩容优化
                    2.引入和红黑树,【避免单条链表过长而影响查询效率】
                    3.决了多线程死循环问题,但仍非线程安全的,多线程可能导致数据丢失问题
        put方法的具体流程
            路程图
                
            描述
                1.判断键值对数组table是否为空或者null,否则执行resize()
                2.根据key计算hash值得到插入的数组索引i,如果table[i]== null直接新建节点添加,进入第6步,否者进入第三步
                3.判断table[i]的首个元素是否和key一样,如果相同则覆盖,不同进入第4步【相同指 hashcode以及equals比较】
                4.判断table[i]是否为红黑树treeNode,如果是则在树中插入键值对,否则进入第5步
                5.遍历table[i],判断链表长度是否大于阈值,大于将链表转化为红黑树,在红黑树中插入,否则进入链表进行插入;遍历中如果key已经存在则直接覆盖
                6.插入成功后,判断实际存在的键值对数量size是否超出最大容量threshold,如果超过进行扩容【resize()】
        扩容是怎么实现的
            1.在jdk1.8中,resize()方法是在hashMap中的键值对大于阈值时或者初始化时,调用热size方法进行扩容
            2.每次扩容的时候,都是扩展2倍
            3.扩展后node的位置要么是在原位置,要么是移动到原偏移量2倍的位置
            在purVal()中,有两次使用了resize(),在进行初始化时会进行扩容或者当该数组的实际大小大于其临界值(第一次为12 16*0.75),这个时候扩容的同时也会伴随的桶上面的元素进行重新分发,这也是jdk1.8优化的一个点;在1.7中扩容之后需要重新计算hash值,根据hash值重新分发,但是在1.8中则是根据同一个桶的位置中进行判断【e.hash & oldCap)是否为0,重新进行分配后,该元素要么停留在原位,要么移动到原始位置+增加的数据大小这个位置上
        HashMap是怎么解决哈希冲突
            什么是哈希?
                一般称为“散列”;就是把任意长度的输入通过散列算法,变换成固定长度的输出,该输出就是散列值(哈希值)【简单说:就是讲任意长度的消息压缩成固定长度消息的函数】
                基本特性:同一散列函数计算出的散列值如果不同,那么输入值肯定也不同,但是如果相同,那么输入值也不一定相同
            什么是哈希冲突?
                当两个不同的输入值,根据同一个散列函数计算出相同的散列值
            哪些方法解决?
                1.使用链地址法(散列表)来链接拥有相同hash值得数据
                2.使用2次扰动函数(hash())来降低hash冲突的概率,是的数据分布更平均
                3.引入红黑树进一步降低遍历的时间复杂度,提升遍历速度
        能否使用任意类作为Map的key
            可以使用任意的类型作为key,但是要考虑以下几点!
                如果类重写了equals(),一定也要重写hashCode()方法
                类的所有实例都要遵循与equals()和hashCode()先关规则
                如果一个类中没有使用equals(),不应该在hashCode()中使用他
                用户自定义key类最佳实践是使之为不可变的,这样hashCode()值可以被缓存起来,拥有更好的性能。【不可变的类可以确保hashCode()和equals()在未来不会改变,这样就可以解决与可变相关的问题了】
        为什么String、Integer这样的包装类适合作为key
            它能够保证Hash值不可更改性和及计算准确性,能够有效的减少Hash碰撞的概率
                内部已重写了equals()和hashCode()等方法;遵循了HashMap内部的规范,不容易出现hash值计算错误的情况
        如果使用了Object作为HashMap的key应该怎么办?
            重写equals()和hashCode()方法
        HashMap为什么不直接使用hashcode()处理后的哈希值作为下标
            hashCode方法返回的是int整数类型,其取值范围大约有40亿,而hashMap的最大容量范围取不到最大值,会导致计算出的哈希值不在数组的大小范围,进而无法匹配存储位置
            解决方案
                HashMap实现了自己的hash()方法
        HashMap的长度为什么是2的幂次方
            为了能让HashMap存取高效,尽量减少碰撞,也就是尽量把数据分配均匀,每个链表、红黑树的长度大致相同
        HashMap和HashTable有什么区别
            线程安全
                hashMap非线程安全,hashTable是线程安全的,其内部的方法都经过synchronized修饰
            效率
                因为线程安全问题,hashMap比hashTable效率更高,且hashTable基本被淘汰
            为null key和null value的支持
                hashMap可以支持,但只能有一个,hashTable不支持,只要有null直接回抛出异常
            初始容量大小和每次扩容的大小
                1.如果创建时未指定容量初始值,hashtable默认值为11,之后每次扩容容量为原来的2n+1;hashMap为16,之后每次扩容都是原来的两倍
                2.创建时如果给了默认长度,hashTable会直接使用给的长度;而HashMap会将其扩充为2的幂次方大小。也就是说HashMap总是使用2的幂次方作为哈希表的大小
            底层数据结构
                hashMap在1.8中当链表长度到达阈值会转换为红黑树,hashTable不会
            推荐使用:hashTable作为保留类不建议使用,推荐在单线程的环境下使用hashMap,如果多线程使用ConcurrentHashMap
        如何决定使用HashMap还是TreeMap
            如果你需要一个有序的key集合进行遍历使用treeMap
            在map中插入、删除、定位元素这类操作HashMap是最好的选择
        ConCurrentHashMap和HashMap的区别
            线程安全
                ConcurrentHashMap对整个桶数组进行分割分段(segment),然后在每一个分段上都用lock锁进行保护,相对于hashTable的同步锁(synchronized)锁粒度更精细,并发性能更好。【jdk1.8之后利用了cas算法】
                HashMap没有锁机制,不是线程安全的。
            键值对允许null
                HashMap的键值对允许有null,但是concurrentHashMap都不允许
        ConCurrentHashMap和HashTable的区别
            底层数据结构
                ConcurrentHashMap
                    JDK1.8之前采用分段数组+链表实现,JDK1.8之后和HashMap的结构一样,采用数组+链表+红黑树
                HashTable
                    数组+链表;数组是主体,链表是为解决哈希冲突而存在
            实现线程安全的方式
                ConcurrentHashMap
                    在jdk1.7时,concurrentHashMap(分段锁)对整个桶数组进行了分割分段(segment),每一把锁只锁住容器的其中一部分数据,多线程访问容器的不同数据,就不会存在锁竞争,提高并发效率。【默认分配16个segment,比hashTable性能提高了16倍】。到了jdk1.8时已经摒弃了segment的概念,而是直接使用node数组+链表+红黑树的数据结构来实现,并发控制使用synchronized和CAS来操作。(JDK1.6之后对synchronized锁做了很多优化;整个看起来就是一个线程安全的HashMap,虽然在JDK1.8中还能看到segment的数据结构,但是已经简化了属性,只是为了兼容低版本
                HashTable(同一把锁)使用synchronized来保证线程安全,效率低下,当一个线程访问同步方法是,其他线程无法访问同步方法,可能会进入阻塞或者轮询状态;如使用put添加元素,另一个线程不能使用put添加,也不能使用get,会导致竞争越来越激烈,效率越低。
        ConCurrentHashMap的实现原理
            JDK1.7
                概述:
                    将数据分为一段一段的存储,然后每一个段都配一把锁,当一个线程占用锁访问其中一段数据时,其他的段数据也能被其他线程访问
                实现:采用segment+ hashEntry
                    
                    一个ConcurrentHashMap里包含一个segment数组,segment结构和hashmap类似,是一种数组+链表的结构,一个segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个segment守护着HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须先获取对应的segment的锁
            JDK1.8
                概述
                    放弃了segment臃肿的设计,采用Node+CAS+Synchronized来保证并发安全。synchronized只锁住当前链表或者红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升了n倍
                实现
                    
            1.该类包含两个静态内部类HashEntry(封装映射表的键值对)和Segment(充当锁的角色);
2.Segment是一种可重入的锁ReentrantLock,每个segment守护一个HashEntry数组里的元素,当要对HashEntry数组的数据进行修改时,必须先获取对应得segment锁
        什么是CAS
            CAS是compare and swap 比较交换
            CAS是一种基于锁的操作;而且是乐观锁。CAS操作包含三个操作数;内存位置(V)、预期原值(A)和新值(B),如果内存地址的值和A值是一样的,那么就将内存里的值更新为B,CAS是通过无限循环来获取数据的,如果在第一轮中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能有机会执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值