ArrayList和linkedList内部实现⼤致是怎样的?他们之间的区别和优缺点?Hashmap实现原理?如何保证HashMap线程安全?Hashmap扩容机制NIO是什么?适⽤于何种场景?String str=new String("abc") 创建了几个String对象?在java中⼀个字符能否表示⼀个汉字java反射机制 ArrayList和linkedList内部实现⼤致是怎样的?他们之间的区别和优缺点?
a、ArrayList :内部使⽤数组的形式实现了存储,利⽤数组的下标进⾏元素的访问,因此对元素的随机访问速度⾮常快。因为是 数组,所以ArrayList在初始化的时候,有初始⼤⼩10,插⼊新元素的时候,会判断是否需要扩容,扩容的步⻓是0.5倍原容量, 扩容⽅式是利⽤数组的复制 。b、linkedList :内部使⽤双向链表的结构实现存储。 linkedList包含两个重要的成员:header 和 size。
header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。size是双向链表中节点的个数。 ArrayList查找较快,插⼊、删除较慢,linkedList查找较慢,插⼊、删除较快。 Hashmap实现原理?如何保证HashMap线程安全?
a、HashMap简单说就是它根据键的hashCode值存储数据,⼤多数情况下可以直接定位到它的值,因⽽具有很快的访问速度, 但遍历顺序却是不确定的。b、HashMap基于哈希表,底层结构由数组 + 链表 + 红黑树 来实现,添加到集合中的元素以“key–value”形式保存到数组中,在数组中key-- value被包装成⼀个实体来处理–Entryc、在HashMap中,Entry[]保存了集合中所有的键值对,当我们需要快速存储、获取、删除集合中的元素时,HashMap会根据 hash算法来获得“键值对”在数组中存在的位置,以来实现对应的操作⽅法d、HashMap添加元素:将准备增加到map中的对象与该位置上的对象进⾏⽐较(equals⽅法),如果相同,那么就将该位置上的那个对象(Entry类型)的value值替换掉,否则沿着该Entry的链继续重复上述过程,如果到链的最后任然没有找到与此对象相同的对象,那么这个时候就会被增加到数组中,将数组中该位置上的那个Entry对象链到该对象的后⾯(先hashcode计算位置,如果找到相同位置便替换值,找不到则重复hashcode计算,直到最后在添加到hashmap最后⾯ )e、HashMap是基于哈希表的Map接⼝的⾮同步实现,允许null键值,但不保证映射的顺序;底层使⽤数组实现,数组中的每项是⼀个链表;存储时根据key的hash算法来决定其存储位置;数组扩容需要重新计算扩容后每个元素在数组中的位置很耗性能;f、ConcurrentHashMap是HashMap的线程安全实现,允许多个修改操作同时进⾏(使⽤了锁分离技术),它使⽤了多个锁来控制对hash表的不同段进⾏的修改,每个段其实就是⼀个⼩的hashtable,它们有⾃⼰的锁。使⽤了多个⼦hash表(段Segment),允许多个读操作并发进⾏,读操作并不需要锁,因为它的HashEntry⼏乎是不可变的。 Hashmap扩容机制
构造方法
给定初始容量和加载因子给定初始容量,使用默认的加载因子无参, 使用默认的初始容量和默认的加载因子传进一个Map,使用默认的加载因子 初始化容量
当你调用HashMap的构造方法时,HashMap是没有进行初始化容量,也就是现在是一个空的HashMap(容量为0),这是因为HashMap使用的懒加载机制,只有你第一次向HashMap中添加元素时,才进行第一次的容量设置调用 resize() 方法对HashMap进行容量设置,此时会有两种情况的容量初始化。
第一种情况:当我们没有设置初始化容量时,HashMap就使用默认的初始化容量,也就是16
第二种情况:当我们设置了初始化容量,HashMap就会按照我们设置的容量进行设置吗?答案是不一定。当你设置的初始化容量是2的n次方时,就会按照你设置的容量设置;当你设置的初始化容量不是2的n次方时,就会按照大于你设置的那个值但是最接近你设置的那个值的2的n次方进行设置。
扩容
capacity 即容量,默认16。loadFactor 加载因子,默认是0.75threshold 阈值。阈值=容量*加载因子。默认12。当元素数量超过阈值时便会触发扩容。每次扩容的容量都是之前容量的2倍。 NIO是什么?适⽤于何种场景?
a、NIO是为了弥补IO操作的不⾜⽽诞⽣的,NIO的⼀些新特性有:⾮阻塞I/O,选择器,缓冲以及管道。b、如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,这时候⽤NIO处理数据可能是个很好的选择。(适⽤于⼩数据多连接)c、⽽如果只有少量的连接,⽽这些连接每次要发送⼤量的数据,这时候传统的IO更合适。使⽤哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作⽐较来权衡选择。d、NIO:
概念:NIO(new IO),是⼀种⾮阻塞式I/O;java NIO采⽤了双向通道进⾏数据传输,在通道上我们可以注册我们感兴趣的事件:连接事件、读写事件;NIO主要有三⼤核⼼部分:Channel(通道),Buffer(缓冲区), Selector(选择器)。传统IO基于字节流和字符流进⾏操作,⽽NIO基于Channel和Buffer(缓冲区)进⾏操作,数据总是从通道读取到缓冲区中,或者从缓冲区
写⼊到通道中。Selector(选择区)⽤于监听多个通道的事件(⽐如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。原理: 由⼀个专⻔的线程来处理所有的 IO 事件,并负责分发。事件驱动机制:事件到的时候触发,⽽不是同步的去监视事件。线程通讯:线程之间通过 wait,notify 等⽅式通讯。保证每次上下⽂切换都是有意义的。减少⽆谓的线程切换。 String str=new String(“abc”) 创建了几个String对象?
2个。 代码分成String str、=、"abc"和new String()四部分来看待。
String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给它,显然也没有创建对象;new String(“abc”)为什么能被看成"abc"和new String()
引号内包含文本,是 一种创建String对象的方式, 这 种方式是String特有的,并且它与new的方式存在很大区别。
String str=“abc”;
毫无疑问,这行代码创建了一个String对象。
String a=“abc”; String b=“abc”; 那这里呢?
答案还是一个。
String a=“ab”+“cd”; 再看看这里呢? 答案是三个。
字符串池
在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。
再看String a=“abc”;,这行代码被执行的时候,JAVA虚拟机首先在字符串池中查找是否已经存在了值为"abc"的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
只有使用引号包含文本的方式创建的String对象或者String之间使用“+”连接产生的新对象才会被加入字符串池中。
在java中⼀个字符能否表示⼀个汉字在java中,⼀个字符表示16位,相当于2个字节,⼀个汉字(默认gbk)正好是2个字节。utf-8是3个字节。
java反射机制可以在运⾏时判断⼀个对象所属的类,构造⼀个类的对象,判断类具有的成员变量和⽅法,调⽤对象的⽅法。4个关键的类:Class,Constructor,Field,Method。getConstructor获得构造函数/getDeclardConstructor;getField/getFields/getDeclardFields获得类所声明的所有字段;getMethod/getMethods/getDeclardMethod获得类声明的所有⽅法