Skip to content

Commit 5fdb3f8

Browse files
committed
[fix]图片地址出错
1 parent f2c3a98 commit 5fdb3f8

File tree

1 file changed

+1
-228
lines changed

1 file changed

+1
-228
lines changed

docs/java/collection/Java集合框架常见面试题.md

Lines changed: 1 addition & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010
- [1.1.3.3. Map](#1133-map)
1111
- [1.1.4. 如何选用集合?](#114-如何选用集合)
1212
- [1.1.5. 为什么要使用集合?](#115-为什么要使用集合)
13-
- [1.1.6. Iterator 迭代器](#116-iterator-迭代器)
14-
- [1.1.6.1. 迭代器 Iterator 是什么?](#1161-迭代器-iterator-是什么)
15-
- [1.1.6.2. 迭代器 Iterator 有啥用?](#1162-迭代器-iterator-有啥用)
16-
- [1.1.6.3. 如何使用?](#1163-如何使用)
17-
- [1.1.7. 有哪些集合是线程不安全的?怎么解决呢?](#117-有哪些集合是线程不安全的怎么解决呢)
1813
- [1.2. Collection 子接口之 List](#12-collection-子接口之-list)
1914
- [1.2.1. Arraylist 和 Vector 的区别?](#121-arraylist-和-vector-的区别)
2015
- [1.2.2. Arraylist 与 LinkedList 区别?](#122-arraylist-与-linkedlist-区别)
@@ -46,13 +41,6 @@
4641
- [1.5.1. 排序操作](#151-排序操作)
4742
- [1.5.2. 查找,替换操作](#152-查找替换操作)
4843
- [1.5.3. 同步控制](#153-同步控制)
49-
- [1.6. 其他重要问题](#16-其他重要问题)
50-
- [1.6.1. 什么是快速失败(fail-fast)?](#161-什么是快速失败fail-fast)
51-
- [1.6.2. 什么是安全失败(fail-safe)呢?](#162-什么是安全失败fail-safe呢)
52-
- [1.6.3. Arrays.asList()避坑指南](#163-arraysaslist避坑指南)
53-
- [1.6.3.1. 简介](#1631-简介)
54-
- [1.6.3.2. 《阿里巴巴 Java 开发手册》对其的描述](#1632-阿里巴巴-java-开发手册对其的描述)
55-
- [1.6.3.3. 使用时的注意事项总结](#1633-使用时的注意事项总结)
5644

5745
<!-- /TOC -->
5846

@@ -112,58 +100,7 @@
112100
因为我们在实际开发中,存储的数据的类型是多种多样的,于是,就出现了“集合”,集合同样也是用来存储多个数据的。
113101

114102
数组的缺点是一旦声明之后,长度就不可变了;同时,声明数组时的数据类型也决定了该数组存储的数据的类型;而且,数组存储的数据是有序的、可重复的,特点单一。
115-
但是集合提高了数据存储的灵活性,Java 集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据
116-
117-
### 1.1.6. Iterator 迭代器
118-
119-
#### 1.1.6.1. 迭代器 Iterator 是什么?
120-
121-
```java
122-
public interface Iterator<E> {
123-
//集合中是否还有元素
124-
boolean hasNext();
125-
//获得集合中的下一个元素
126-
E next();
127-
......
128-
}
129-
```
130-
131-
`Iterator` 对象称为迭代器(设计模式的一种),迭代器可以对集合进行遍历,但每一个集合内部的数据结构可能是不尽相同的,所以每一个集合存和取都很可能是不一样的,虽然我们可以人为地在每一个类中定义 `hasNext()``next()` 方法,但这样做会让整个集合体系过于臃肿。于是就有了迭代器。
132-
133-
迭代器是将这样的方法抽取出接口,然后在每个类的内部,定义自己迭代方式,这样做就规定了整个集合体系的遍历方式都是 `hasNext()``next()`方法,使用者不用管怎么实现的,会用即可。迭代器的定义为:提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节。
134-
135-
#### 1.1.6.2. 迭代器 Iterator 有啥用?
136-
137-
`Iterator` 主要是用来遍历集合用的,它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 `ConcurrentModificationException` 异常。
138-
139-
#### 1.1.6.3. 如何使用?
140-
141-
我们通过使用迭代器来遍历 `HashMap`,演示一下 迭代器 Iterator 的使用。
142-
143-
```java
144-
145-
Map<Integer, String> map = new HashMap();
146-
map.put(1, "Java");
147-
map.put(2, "C++");
148-
map.put(3, "PHP");
149-
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
150-
while (iterator.hasNext()) {
151-
Map.Entry<Integer, String> entry = iterator.next();
152-
System.out.println(entry.getKey() + entry.getValue());
153-
}
154-
```
155-
156-
### 1.1.7. 有哪些集合是线程不安全的?怎么解决呢?
157-
158-
我们常用的 `Arraylist` ,`LinkedList`,`Hashmap`,`HashSet`,`TreeSet`,`TreeMap``PriorityQueue` 都不是线程安全的。解决办法很简单,可以使用线程安全的集合来代替。
159-
160-
如果你要使用线程安全的集合的话, `java.util.concurrent` 包中提供了很多并发容器供你使用:
161-
162-
1. `ConcurrentHashMap`: 可以看作是线程安全的 `HashMap`
163-
2. `CopyOnWriteArrayList`:可以看作是线程安全的 `ArrayList`,在读多写少的场合性能非常好,远远好于 `Vector`.
164-
3. `ConcurrentLinkedQueue`:高效的并发队列,使用链表实现。可以看做一个线程安全的 `LinkedList`,这是一个非阻塞队列。
165-
4. `BlockingQueue`: 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。
166-
5. `ConcurrentSkipListMap` :跳表的实现。这是一个`Map`,使用跳表的数据结构进行快速查找。
103+
但是集合提高了数据存储的灵活性,Java 集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据。
167104

168105
## 1.2. Collection 子接口之 List
169106

@@ -675,170 +612,6 @@ synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安
675612
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的)set。
676613
```
677614

678-
## 1.6. 其他重要问题
679-
680-
### 1.6.1. 什么是快速失败(fail-fast)?
681-
682-
**快速失败(fail-fast)** 是 Java 集合的一种错误检测机制。**在使用迭代器对集合进行遍历的时候,我们在多线程下操作非安全失败(fail-safe)的集合类可能就会触发 fail-fast 机制,导致抛出 `ConcurrentModificationException` 异常。 另外,在单线程下,如果在遍历过程中对集合对象的内容进行了修改的话也会触发 fail-fast 机制。**
683-
684-
> 注:增强 for 循环也是借助迭代器进行遍历。
685-
686-
举个例子:多线程下,如果线程 1 正在对集合进行遍历,此时线程 2 对集合进行修改(增加、删除、修改),或者线程 1 在遍历过程中对集合进行修改,都会导致线程 1 抛出 `ConcurrentModificationException` 异常。
687-
688-
**为什么呢?**
689-
690-
每当迭代器使用 `hashNext()`/`next()`遍历下一个元素之前,都会检测 `modCount` 变量是否为 `expectedModCount` 值,是的话就返回遍历;否则抛出异常,终止遍历。
691-
692-
如果我们在集合被遍历期间对其进行修改的话,就会改变 `modCount` 的值,进而导致 `modCount != expectedModCount` ,进而抛出 `ConcurrentModificationException` 异常。
693-
694-
> 注:通过 `Iterator` 的方法修改集合的话会修改到 `expectedModCount` 的值,所以不会抛出异常。
695-
696-
```java
697-
final void checkForComodification() {
698-
if (modCount != expectedModCount)
699-
throw new ConcurrentModificationException();
700-
}
701-
```
702-
703-
好吧!相信大家已经搞懂了快速失败(fail-fast)机制以及它的原理。
704-
705-
我们再来趁热打铁,看一个阿里巴巴手册相关的规定:
706-
707-
![](images/ad28e3ba-e419-4724-869c-73879e604da1.png)
708-
709-
有了前面讲的基础,我们应该知道:使用 `Iterator` 提供的 `remove` 方法,可以修改到 `expectedModCount` 的值。所以,才不会再抛出`ConcurrentModificationException` 异常。
710-
711-
### 1.6.2. 什么是安全失败(fail-safe)呢?
712-
713-
明白了快速失败(fail-fast)之后,安全失败(fail-safe)我们就很好理解了。
714-
715-
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。所以,在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛 `ConcurrentModificationException` 异常。
716-
717-
### 1.6.3. Arrays.asList()避坑指南
718-
719-
最近使用`Arrays.asList()`遇到了一些坑,然后在网上看到这篇文章:[Java Array to List Examples](http://javadevnotes.com/java-array-to-list-examples) 感觉挺不错的,但是还不是特别全面。所以,自己对于这块小知识点进行了简单的总结。
720-
721-
#### 1.6.3.1. 简介
722-
723-
`Arrays.asList()`在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个 List 集合。
724-
725-
```java
726-
String[] myArray = { "Apple", "Banana", "Orange" };
727-
List<String> myList = Arrays.asList(myArray);
728-
//上面两个语句等价于下面一条语句
729-
List<String> myList = Arrays.asList("Apple","Banana", "Orange");
730-
```
731-
732-
JDK 源码对于这个方法的说明:
733-
734-
```java
735-
/**
736-
*返回由指定数组支持的固定大小的列表。此方法作为基于数组和基于集合的API之间的桥梁,与 Collection.toArray()结合使用。返回的List是可序列化并实现RandomAccess接口。
737-
*/
738-
public static <T> List<T> asList(T... a) {
739-
return new ArrayList<>(a);
740-
}
741-
```
742-
743-
#### 1.6.3.2. 《阿里巴巴 Java 开发手册》对其的描述
744-
745-
`Arrays.asList()`将数组转换为集合后,底层其实还是数组,《阿里巴巴 Java 开发手册》对于这个方法有如下描述:
746-
747-
![阿里巴巴Java开发手-Arrays.asList()方法](<https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/阿里巴巴Java开发手-Arrays.asList()方法.png>)
748-
749-
#### 1.6.3.3. 使用时的注意事项总结
750-
751-
**传递的数组必须是对象数组,而不是基本类型。**
752-
753-
`Arrays.asList()`是泛型方法,传入的对象必须是对象数组。
754-
755-
```java
756-
int[] myArray = { 1, 2, 3 };
757-
List myList = Arrays.asList(myArray);
758-
System.out.println(myList.size());//1
759-
System.out.println(myList.get(0));//数组地址值
760-
System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException
761-
int [] array=(int[]) myList.get(0);
762-
System.out.println(array[0]);//1
763-
```
764-
765-
当传入一个原生数据类型数组时,`Arrays.asList()` 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时 List 的唯一元素就是这个数组,这也就解释了上面的代码。
766-
767-
我们使用包装类型数组就可以解决这个问题。
768-
769-
```java
770-
Integer[] myArray = { 1, 2, 3 };
771-
```
772-
773-
**使用集合的修改方法:`add()``remove()``clear()`会抛出异常。**
774-
775-
```java
776-
List myList = Arrays.asList(1, 2, 3);
777-
myList.add(4);//运行时报错:UnsupportedOperationException
778-
myList.remove(1);//运行时报错:UnsupportedOperationException
779-
myList.clear();//运行时报错:UnsupportedOperationException
780-
```
781-
782-
`Arrays.asList()` 方法返回的并不是 `java.util.ArrayList` ,而是 `java.util.Arrays` 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。
783-
784-
```java
785-
List myList = Arrays.asList(1, 2, 3);
786-
System.out.println(myList.getClass());//class java.util.Arrays$ArrayList
787-
```
788-
789-
下图是`java.util.Arrays$ArrayList`的简易源码,我们可以看到这个类重写的方法有哪些。
790-
791-
```java
792-
private static class ArrayList<E> extends AbstractList<E>
793-
implements RandomAccess, java.io.Serializable
794-
{
795-
...
796-
797-
@Override
798-
public E get(int index) {
799-
...
800-
}
801-
802-
@Override
803-
public E set(int index, E element) {
804-
...
805-
}
806-
807-
@Override
808-
public int indexOf(Object o) {
809-
...
810-
}
811-
812-
@Override
813-
public boolean contains(Object o) {
814-
...
815-
}
816-
817-
@Override
818-
public void forEach(Consumer<? super E> action) {
819-
...
820-
}
821-
822-
@Override
823-
public void replaceAll(UnaryOperator<E> operator) {
824-
...
825-
}
826-
827-
@Override
828-
public void sort(Comparator<? super E> c) {
829-
...
830-
}
831-
}
832-
```
833-
834-
我们再看一下`java.util.AbstractList``remove()`方法,这样我们就明白为啥会抛出`UnsupportedOperationException`
835-
836-
```java
837-
public E remove(int index) {
838-
throw new UnsupportedOperationException();
839-
}
840-
```
841-
842615

843616

844617
**《Java面试突击》:** Java 程序员面试必备的《Java面试突击》V3.0 PDF 版本扫码关注下面的公众号,在后台回复 **"面试突击"** 即可免费领取!

0 commit comments

Comments
 (0)