Java面试之集合与IO流三

集合

集合不能存储基本数据类型及java对象,只能存储对象的引用

集合分为两大类
一、单个方式存储元素
Collection超级接口
二、键值对存储方式
Map超级接口

  • Collection
    继承Iterable可迭代的,拥有迭代器
    List接口
    有序可重复:放进去是这个顺序,出来也是这个顺序。元素有下标,可以重复。
    1.ArrayList
    底层是数组,非线程安全;初始化容量为10,每次扩容为原容量的1.5倍;建议预估一个初始化容量,减少数组扩容次数。增删改较慢,查询速度较快
    2.LinkedList
    底层是双向链表,非线程安全;空间上储存不连续;增删改较快,查询较慢
    3.Vector
    底层是数组,线程安全;所有方法都用Synchronized修饰,一般不用
    补充1:LinkedList与ArrayList异同?
    线程安全:LinkedList与ArrayList都是不同步的,也就是不保证线程安全。
    底层数据结构:LinkedList采用双向链表,ArrayList采用Object数组。
    插入和删除是否受位置影响:ArrayList使用的数组,插入和删除时间复杂度受位置影响。LinkedList使用链表,不受影响,时间复杂度近似O(1),而数组的时间复杂度近似O(n)。
    是否支持快速随机访问:LinkedList不支持高效随机访问,ArrayList通过元素的序号快速获取元素对象。
    内存占用:ArrayList浪费空间体现在会在尾部预留一定空间,LinkedList每个元素的占用的空间比ArrayList多,因为要存储当前节点的前驱后继信息。
    补充2:RandomAccess 接口
    查看源代码发现里面什么都没有定义,它是可以快速随机访问的标识,用来表示实现这个接口的类具有随机访问功能。其内因还是底层使用了数组,而不是因为有这个标识所以支持随机访问。
    对list遍历方式总结:
    1.实现了RandomAccess 接口的list, 优先使用普通for循环,其次是foreach.
    2.未实现RandomAccess 接口的list,优先选择iterator遍历(foreach底层也是通过iterator实现的),大size的数据千万不要使用普通for循环。

  • Set接口

无序不可重复:放进去是这个顺序,出来不一定是这个顺序。元素没有下标,不可以有相同的元素。
1.HashSet
创建时实际上是new了HashMap,哈希表数据结构。初始化容量是16,扩容后是原容量的两倍
2.SortedSet
放在该集合中的元素自动排序。
TreeSet继承自SortedSet,底层实际是TreeMap。采用二叉树数据结构

  • Queue接口
    代表先进先出的集合。
    1.PriorityQueue优先级队列
    2.ArrayQueue双端队列
    优先级队列

  • Map
    key、value键值对存储方式,存储的都是java对象的内存地址;无序不可重复。

1.HashMap
线程不安全的;
可以使用一个null作为key,可以多个null作为value
初始化容量为16,默认加载因子0.75。每次扩容必须是2的幂。
HashMap通过key的hashcode经过扰动函数处理后得到hash值。所谓扰动函数就是HashMap的hash方法,用来防止一些实现比较差的hashcode方法,也就是减少hash碰撞。
元素存放位置:Length是HashMap的长度
index = hash & (Length-1)

JDK1.8之前
基于数组+链表实现,底层维护一个Entry数组。
链表的时间复杂度是O(N)
发生hash冲突将KV键值对放到链表头节点,扩容时会改在链表上的顺序,导致链表成环。

JDK1.8之后
基于数组+链表+红黑树,底层维护一个Node数组。
单项链表节点超过8个,单链表转换为红黑树;红黑树节点少于6个时,转换为单链表。红黑树的时间复杂度是O(logN),优化了二叉树查询树的缺陷,二叉查找树在某些情况下会退化成线性结构,提高了查找效率。
发生hash冲突将KV键值对放到链表尾部,解决了扩容成环的问题,即解决了查询的时候出现死循环的情况

put操作:
1.先对hashcCode()做hash,再计算index
2.如果没有碰撞放入node数组中。
3.如果碰撞了,将key用equals的方法与链表的元素进行比较,如果有true则进行覆盖,如果全为false则插入链表尾部。
4.如果碰撞导致链表过长,就会把链表转化为红黑树
5.如果bucket满了(超过当前最大容量 * 负载因子),就要进行resize

get操作
输入key做一次哈希映射,得到Node数组下标。如果当前位置什么都没有,返回null;如果有链表进行equals比较,有ture返回则value值,全为false则返回null,
O(n),如果为红黑树,O(log n)
在进行比较的时候都是使用equals方法,在重写euqals方法是必须要重写hashcode方法,保证相同对象返回相同的hash值。

2.Hashtable
哈希表数据结构,线程安全。所有方法都带有synchronized修饰。
Properties继承自Hashtable,线程安全。存储采用key-value,只能是String
3.LinkedHashMap
是Hash Map的子类,保存记录的插入顺序,在使用Iterator遍历时,得到的是先插入的;也可以在构造方法是带参数,定义按照访问次数排序。
4.TreeMap
实现SortedMap接口,可以根据key排序,默认是升序,也可以指定比较器。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap时传入自定义的Comparator,否则会在运行时抛出异常。当使用Iterator遍历TreeMap时,得到的记录是排过序的;如果使用排序的映射,建议使用TreeMap。

补充:ConcurrentHashMap

  • 1.8之前
    使用分段数组+链表实现,一个ConcurrentHashMap里面有多个Segment,每个Segment里面是一个HashEntry数组,HashEntry数组用来存储键值对数据。
    每个Segment有一把锁,它实现了RenntrantLock,是一种可重入的锁。要对HashEntry中的数据进行修改时,必须获得Segment锁。
    get
    先定位到Segment,再通过hash定位到具体的HashEntry上
    HashEntry中的value都是使用volatile修饰的,保证了内存的可见性,每次拿到的value都是最新值。get的整个过程没有加锁
    put
    1.先考虑是否需要扩容
    2.定位在Segment位置,再插入到HashEntry数组中
    插入过程中先会进行一次hash,确定Segment位置,如果Segment还没有初始化,就会使用CAS对它进行赋值。第二次操作定位在HashEntry数组中的位置,在这个过程中会进程锁的特性。在插入要到HashEntry的时候,它会使用继承的ReentrendLock的tryLock方法去获取锁,如果已经有线程获取了Segment锁,它会以自旋的方式接着调用tryLock去尝试获取锁,在尝试一定次数不成功后就会被挂起,等待唤醒。
  • 1.8之后
    底层使用Node数组+链表+红黑树实现实现,与HashMap1.8类似。
    不再使用Segment锁,而是采用CAS+Synchronized锁来保证并发安全。Synchronized锁只锁定当前链表或红黑树的首节点,只要不发生hash冲突就不会产生并发。
    在这里插入图片描述在这里插入图片描述IO流
    输入输出流分为两种:字符流(只能读取文字)、字节流
    以Stream结尾的都为字节流
    以Reader/Writer结尾的都为字符流

文件专属:
FileInputStream
FileOutputStream
FileReader
FileWriter

转换流(将字节流转换为字符流):
InputStreamReader
OutputStreamWriter

缓冲流专属:
BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
缓冲流自带缓冲区,不需要定义byte数组缓存

数据流专属:
DataInputStream
DataOutputStream

标准输出流:
PrintWriter
PrintStream
使用:
1.创建标准输出流对象,参数为需要输出的东西
2.System.setOut,用来选择流输出的位置

对象专属流:
ObjectInputStream
ObjectOutputStream
使对象序列化到硬盘,或者从硬盘中反序列出来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值