-
Collection
与Collections
的区别Collection
是集合类的上级接口,子接口有Set
、List
、LinkedList
、ArrayList
、Vector
、Stack
、Set
;Collections
是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java的Collection
框架。 -
**String
、StringBuffer
和StringBuilder
的区别**String
是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final
类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String
的操作都会生成新的String
对象。每次+操作 : 隐式在堆上new了一个跟原字符串相同的
StringBuilder
对象,再调用append
方法拼接+后面的字符。StringBuffer
和StringBuilder
都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer
和StringBuilder
来进行操作。 另外StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。 -
List,Set,Map
的区别- List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set(注重独一无二的性质):不允许重复的集合。不会有多个元素引用相同的对象。
- Map(用Key来搜索的专家):使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
-
**ArrayList
有什么特点?**Java 集合框架中的一种存放相同类型的元素数据,是一种变长的集合类,基于定长数组实现,当加入数据达到一定程度后,会实行自动扩容,即扩大数组大小。
- 底层是使用数组实现,添加元素。
- 如果
add(o)
,添加到的是数组的尾部,如果要增加的数据量很大,应该使用ensureCapacity()
方法,该方法的作用是预先设置ArrayList
的大小,这样可以大大提高初始化速度。 - 如果使用
add(int,o)
,添加到某个位置,那么可能会挪动大量的数组元素,并且可能会触发扩容机制。
- 如果
高并发的情况下,线程不安全。多个线程同时操作
ArrayList
,会引发不可预知的异常或错误。ArrayList 实现了Cloneable
接口,标识着它可以被复制。注意:ArrayList
里面的clone()
复制其实是浅复制。-
有数组为什么还要
ArrayList
通常我们在使用的时候,如果在不明确要插入多少数据的情况下,普通数组就很尴尬了,因为你不知道需要初始化数组大小为多少,而
ArrayList
可以使用默认的大小,当元素个数到达一定程度后,会自动扩容。可以这么来理解:我们常说的数组是定死的数组,
ArrayList
却是动态数组。
- 底层是使用数组实现,添加元素。
-
ArrayList
和LinkedList
的区别-
ArrayList
优点:
ArrayList
是实现了基于动态数组的数据结构,因为地址连续,一旦数据存好了,查询操作效率会比较高(在内存里是连着放的)。 缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低。 -
LinkedList
优点:
LinkedList
基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作,LinkedList
比较占优势。LinkedList
适用于要头尾操作或插入指定位置的场景。 缺点:因为LinkedList
要移动指针,所以查询操作性能比较低。 -
适用场景分析
- 当需要对数据进行对随机访问的时候,选用
ArrayList
。 - 当需要对数据进行多次增加删除修改时,采用
LinkedList
。
如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用
ArrayList
。 当然,绝大数业务的场景下,使用ArrayList
就够了,但需要注意避免ArrayList
的扩容,以及非顺序的插入。 - 当需要对数据进行对随机访问的时候,选用
-
-
**HashMap
与Hashtable
的区别**- 出生的版本不一样,Hashtable出生于Java发布的第一版本 JDK 1.0,HashMap 出生于 JDK1.2。
- 都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)。
- HashMap 继承的是 AbstractMap,并且 AbstractMap 也实现了 Map 接口。Hashtable 继承Dictionary。
- Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的,是线程安全的,HashMap 是非线程安全的。
- Hashtable 的 key 不能为 null,value 也不能为 null,这个可以从 Hashtable 源码中的 put 方法看到,判断如果 value 为 null 就直接抛出空指针异常,在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况,那说明,这时候如果 key 为空,照样会抛出空指针异常。
- HashMap 的 key 和 value 都可以为 null。在计算 hash 值的时候,有判断,如果key==null ,则其 hash=0 ;至于 value 是否为 null,根本没有判断过。
- Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低。HashMap 为了提高计算效率,将哈希表的大小固定为了 2 的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
- Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式。
- 默认情况下,初始容量不同,Hashtable的初始长度是 11,之后每次扩充容量变为之前的2n+1(n 为上一次的长度)而 HashMap的初始长度为 16,之后每次扩充变为原来的两倍。
-
**HashMap
与HashTable
的区别**- 两者父类不同
HashMap
是继承自AbstractMap
类,而Hashtable
是继承自Dictionary
类。不过它们都实现了同时实现了map
、Cloneable
(可复制)、Serializable
(可序列化)这三个接口。 - 对外提供的接口不同
Hashtable
比HashMap
多提供了elments()
和contains()
两个方法。elments()
方法继承自Hashtable
的父类Dictionnary
。elements()
方法用于返回此Hashtable
中的value
的枚举。contains()
方法判断该Hashtable
是否包含传入的value
。它的作用与containsValue()
一致。事实上,contansValue()
就只是调用了一下contains()
方法。 - 对
null
的支持不同Hashtable
:key和value都不能为null。HashMap:key
可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。 - 安全性不同
HashMap
是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。Hashtable
是线程安全的,它的每个方法上都有synchronized 关键字,因此可直接用于多线程中。 虽然HashMap
是线程不安全的,但是它的效率远远高于Hashtable
,这样设计是合理的,因为大部分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap
。ConcurrentHashMap
虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap
使用了分段锁,并不对整个数据进行锁定。 - 初始容量大小和每次扩充容量大小不同
- 计算hash值的方法不同
- 两者父类不同
-
HashMap
与ConcurrentHashMap
的区别- 都是 key-value 形式的存储数据;
- HashMap 是线程不安全的,ConcurrentHashMap 是 JUC 下的线程安全的;
- HashMap 底层数据结构是数组 + 链表(JDK 1.8 之前)。JDK 1.8 之后是数组 + 链表 + 红黑树。当链表中元素个数达到 8 的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红黑树查询速度快;
- HashMap 初始数组大小为 16(默认),当出现扩容的时候,以 0.75 * 数组大小的方式进行扩容;
- ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment + HashEntry,Segment 数组大小默认是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized来保证并发安全进行实现。
-
HashMap
长度为什么是 2 的 N 次方呢?为了能让 HashMap 存数据和取数据的效率高,尽可能地减少 hash 值的碰撞,也就是说尽量把数据能均匀的分配,每个链表或者红黑树长度尽量相等。我们首先可能会想到 % 取模的操作来实现。
取余(%)操作中如果除数是 2 的幂次,则等价于与其除数减一的与(&)操作(也就是说hash % length == hash &(length - 1) 的前提是 length 是 2 的 n 次方)。并且,采用二进制位操作 & ,相对于 % 能够提高运算效率。这就是为什么 HashMap 的长度需要 2 的 N 次方了。
-
HashMap
的key
可以用任何类作为key
吗?自定义类HashMap
的key
需要注意什么?- 如果类重写了 equals 方法,它也应该重写 hashCode 方法。
- 类的所有实例需要遵循与 equals 和 hashCode 相关的规则。
- 如果一个类没有使用 equals,你不应该在 hashCode 中使用它。
- 咱们自定义 key 类的最佳实践是使之为不可变的,这样,hashCode 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode 和 equals 在未来不会改变,这样就会解决与可变相关的问题了。
-
**Object
常用方法**-
clone
方法保护方法,实现对象的浅复制,只有实现了
Cloneable
接口才可以调用该方法,否则抛出CloneNotSupportedException
异常,深拷贝也需要实现Cloneable
,同时其成员变量为引用类型的也需要实现Cloneable
,然后重写clone
方法。 -
finalize
方法该方法和垃圾收集器有关系,判断一个对象是否可以被回收的最后一步就是判断是否重写了此方法。
-
equals
方法该方法使用频率非常高。一般
equals
和==
是不一样的,但是在Object
中两者是一样的。子类一般都要重写这个方法。 -
hashCode
方法该方法用于哈希查找,重写了
equals
方法一般都要重写hashCode
方法,这个方法在一些具有哈希功能的Collection
中用到。 一般必须满足obj1.equals(obj2)==true
。可以推出obj1.hashCode()==obj2.hashCode()
,但是hashCode
相等不一定就满足equals
。不过为了提高效率,应该尽量使上面两个条件接近等价。 -
wait
方法配合
synchronized
使用,wait
方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()
方法一直等待,直到获得锁或者被中断。wait(long timeout)
设定一个超时间隔,如果在规定时间内没有获得锁就返回。-
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
- 其他线程调用了该对象的
notify
方法; - 其他线程调用了该对象的
notifyAll
方法; - 其他线程调用了
interrupt
中断该线程; - 时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个
InterruptedException
异常。 - 其他线程调用了该对象的
-
-
notify
方法配合
synchronized
使用,该方法唤醒在该对象上等待队列中的某个线程(同步队列中的线程是给抢占 CPU 的线程,等待队列中的线程指的是等待唤醒的线程)。 -
notifyAll
方法配合
synchronized
使用,该方法唤醒在该对象上等待队列中的所有线程。 -
总结
只要把上面几个方法熟悉就可以了,
toString
和getClass
方法可以不用去讨论它们。该题目考察的是对Object
的熟悉程度,平时用的很多方法并没看其定义但是也在用,比如说:wait()
方法,equals()
方法等。Object
是所有类的根,是所有类的父类,所有对象包括数组都实现了Object
的方法。
-
-
红黑树的特征?
- 每个节点是黑色或红色
- 根节点是黑色
- 每个叶子节点都是黑色(指向空的叶子节点)
- 如果一个叶子节点是红色,那么其子节点必须都是黑色的
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
Java基础学习笔记(六)完
最新推荐文章于 2024-07-18 16:29:57 发布