赞
踩
Java 集合
1、说说 List, Set, Queue, Map 四者的区别?
y=f(x),"x" 代表 key,"y" 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值
2、如何选用集合?
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用 Map 接口下的集合,需要排序时选择 TreeMap,不需要排序时就选择 HashMap,需要保证线程安全就选用 ConcurrentHashMap。
当我们只需要存放元素值时,就选择实现 Collection 接口的集合,需要保证元素唯一时选择实现 Set 接口的集合比如 TreeSet 或 HashSet,不需要就选择实现 List 接口的比
如 ArrayList 或 LinkedList,然后再根据实现这些接口的集合的特点来选用。
3、为什么要使用集合?
当我们需要保存一组类型相同的数据的时候,我们应该是用一个容器来保存,这个容器就是数组,但是,使用数组存储对象具有一定的弊端, 因为我们在实际开发中,存储的数据的类型是多种多样的,于是,就出现了“集合”,集合同样也是用来存储多个数据的。
数组的缺点是一旦声明之后,长度就不可变了;同时,声明数组时的数据类型也决定了该数组存储的数据的类型;而且,数组存储的数据是有序的、可重复的,特点单一。 但是集合提高了数据存储的灵活性,Java 集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据。
4、 Arraylist 和 Vector 的区别?
5、Arraylist 与 LinkedList 区别?
(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
近似 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element),remove(Object o)) 时间复杂度近似为 O(n) ,因为需要先移动到指定
位置再插入。
间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
6、Comparable 和 Comparator 的区别?
obj2)方法用来排序
一般我们需要对一个集合使用自定义排序时,我们就要重写 compareTo()方法或
compare()方法,当我们需要对某一个集合实现两种排序方式,比如一个 song 对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写 compareTo()方法和使用自制的
Comparator 方法或者以两个 Comparator 来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 Collections.sort().
7、无序性和不可重复性的含义是什么
8、比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
9、试比较 Queue 与 Deque 的区别
Queue 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出
(FIFO) 规则。
Queue 扩展了 Collection 的接口,根据 因为容量问题而导致操作失败后处理方式的不同 可以分为两类方法: 一种在操作失败后会抛出异常,另一种则会返回特殊值。
Queue 接口 | 抛出异常 | 返回特殊值 |
插入队尾 | add(E e) | offer(E e) | |
删除队首 | remove() | poll() | |
查询队首元素 | element() | peek() |
Deque 是双端队列,在队列的两端均可以插入或删除元素。
Deque 扩展了 Queue 的接口, 增加了在队首和队尾进行插入和删除的方法,同样根据失败后处理方式的不同分为两类:
事实上,Deque 还提供有 push() 和 pop() 等其他方法,可用于模拟栈。
10、请谈一下对 PriorityQueue 的认识?
PriorityQueue 是在 JDK1.5 中被引入的, 其与 Queue 的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。
这里列举其相关的一些要点:
11、HashMap 和 Hashtable 的区别?
为 Hashtable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
Hashtable 基本被淘汰,不要在代码中使用它;
作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。
说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
12、HashSet 如何检查重复?
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,
HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用
equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。
在 openjdk8 中,HashSet 的 add()方法只是简单的调用了 HashMap 的 put()方法,并且判断了一下返回值以确保是否有重复元素。 也就是说,在 openjdk8 中,实际上无论
HashSet 中是否已经存在了某元素,HashSet 都会直接插入,只是会在 add()方法的返回值处告诉我们插入前是否存在相同元素。
13、HashMap 的长度为什么是 2 的幂次方?
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个
40 亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ (n - 1) & hash”。(n 代表数组长度)。这也就解释了 HashMap 的长度为什么是 2 的幂次方。
这个算法应该如何设计呢?
我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。
14、ConcurrentHashMap 和 Hashtable 的区别?
ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
JDK1.8 采用的数据结构跟 HashMap1.8 的结构一样,数组+链表/红黑二叉树。
Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;
② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
15、ConcurrentHashMap 线程安全的具体实现方式是怎样的?
JDK1.7
首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。
Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。
HashEntry 用于存储键值对数据。
static class Segment<K,V> extends ReentrantLock implements Serializable {
}
一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构
和 HashMap 类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 的锁。JDK1.8
ConcurrentHashMap 取消了 Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为O(log(N)))
synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。
16、TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?
TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进行排序。
Collections 工具类的 sort 方法有两种重载的形式:
第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较。
第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是
Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。
17、Collection 和 Collections 有什么区别?
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection 接口在 Java 类库中有很多具体的实现。Collection 接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有 List 与
Set。
Collections 则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
18、Array 和 ArrayList 有何区别?
¡ Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
ArrayList 有。
对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
19、HashMap 和 ConcurrentHashMap 的区别
ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用 lock 锁进行保护,相对于 HashTable 的 synchronized 锁的粒度更精细了一些,并发性能更好,而 HashMap 没有锁机制,不是线程安全的。(JDK1.8 之后ConcurrentHashMap 启用了一种全新的方式实现,利用 CAS 算法。)
HashMap 的键值对允许有 null,但是 ConCurrentHashMap 都不允许。
20、如果使用 Object 作为 HashMap 的 Key,应该怎么办呢?
重写 hashCode()和 equals()方法
21、为什么 HashMap 中 String、Integer 这样的包装类适合作为 K?
String、Integer 等包装类的特性能够保证 Hash 值的不可更改性和计算准确性,能够有效的减少 Hash 碰撞的几率。
以去上面看看 putValue 的过程),不容易出现 Hash 值计算错误的情况;
22、什么是哈希冲突?
当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。
fail-fast 是 Java 中的一种 快速失败 机制,java.u til 包下所有的集合都是快速失败的,快速失败会抛出 ConcurrentModificationException 异常,fa ail-fast 你可以把它理解为一种快速检测机制它只能用来检测错误,不会对错误进行恢复,fail-fast 不一定只在多线程 环境下存在,ArrayList 也会抛出这个异常,主要原因是由于 modCount 不等于 ex
pectedModCount。
fail-safe 是 Java 中的一种安全失败机制,它表示的是在遍历时不是直接在原集合上进行访问,而是先复制原有集合内容,在拷贝的集合上进行遍历。 由于迭代时是对原集合的拷贝进行遍历,所以在 遍历过程中对原集合所作的修改并不能被迭代器检测到 所以不会触
发 ConcurrentModificationException。java.util.conc urrent 包下的容器都是安全失败的, 可以在多线程条件下使用,并发修改。
Arrays.asList 是 Array 中的一个静态方法,它能够实现把数组转换成为 List 序列,需要注意下面几点:
public static void main(String[] args) { Integer[] integer = new Integer[] { 1, 2, 3, 4 }; List integetList = Arrays.asList(integer);
integetList.add(5);
}
// 结果抛出异常
// Exception in thread "main" java.lang.UnsupportedOperationException
我们看源码就很容易发现问题:
// 这是 java.util.Arrays 内部类,
//而不是 java.util.ArrayList
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable
继承 AbstractList 中对 add、remove、set 方法是直接抛异常的,也就是说如果继承的子
类没有去重写这些方法,那么子类的实例去调用这些方法是会直接抛异常的。
finally 是一个关键字,它经常和 try 块一起使用,用于异常处理。使用 try...finally 的代码块种,
finally 部分的代码一定会被执行,所以我们经常在 finallv 方法中用于资源的关闭操作。
JDK1.7 中,推荐使用 try-with-resources 优雅的关闭资源,它直接使用 try0 进行资源的关闭即可, 就不用写finally 关键字了。
finalize 是 Object 对象中的一个方法,用于对象的回收方法,这个方法我们一般不推荐使用,
finalize 是和垃圾回收关联在一起的,在 Java9 中,将 finalize 标记为了 deprecated,如果没有特别原因,不要实现finalize 方法,也不要指望他来进行垃圾回收。
在 Java 中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。内部类的分类一般主要有四种:
静态内部类就是定义在类内部的静态类,静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;
成员内部类就是定义在类内部,成员位置上的非静态类,就是成员内部类。成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。
定义在方法中的内部类,就是局部内部类。定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。
匿名内部类就是没有名字的内部类,除了没有名字,匿名内部类还有以下特点:
可以,因为 Unicode 编码采用 2 个字节的编码,UTF-8 是Unicode 的一种实现,它使用可变长度的字符集进行编码,charc=中是两个字节,所以能够存储。合法。
代理一般分为静态代理和动态代理,它们都是代理模莫式的一种应用,静态代理指的是在程序运行前已经编译好,程序知道由谁来执行代理方法。
而动态代理只有在程序运行期间才能确定,相比于静态代理,动态代理的优势在于可以很方便的
对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。可以说动态代理是基于反射实现
的。通过反射我们可以直接操作类或者对象,比如获取类的定义,获取声明的属性和方法,调用方
法,在运行时可以修改类的定义。
动态代理是一种在运行时构建代理、动态处理方法调用的机制。动态代理的实现方式有很多,
Java 提供的代理被称为 JDK 动态代理,JDK 动态代理是基于类的继承。
Exception 泛指的是异常,Exception 主要分为两种异常,一种是编译期出现的异常,称为
checkedException,一种是程序运行期间出现的异常常,称为 uncheckedException,常见的
checkedException 有 IOException,uncheckedExc eption 统称为 RuntimeException ,常见的
RuntimeException 主要有 NullPointerException、IllegalArgumentException 、
ArrayIndex0utofB oundException 等,Exception 可以被捕获。
Error 是指程序运行过程中出现的错误,通常情况下会造成程序的崩溃,Error 通常是不可恢复的,Error 不能被捕获。
反射机制就是使 Java 程序在运行时具有自省(introsp ect) 的能力,通过反射我们可以直接操作类 和对象,比如获取某个类的定义,获取类的属性和方法 构造方法等。
创建类实例的三种方式是
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。