【jdk1.8源码分析】总章
一、总章
1.1 前言
本文主要参考这篇文章
另外增加了自己觉得重要的部分
1.2 集合框架概览图
1.3 基础接口源码解析
1.3.1 Iterator
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* 举个简单例子(把集合里的元素每个都输出出来):
* List<String> names = new ArrayList<>();
* names.add("Joemsu");
* names.add("GodnessY");
* names.iterator().forEachRemaining(c -> System.out.println("hi! " + c));
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
可以看到Iterator接口还是很简单的,做到了见名知意,值得一提的是里面的remove方法:此方法可用于在迭代中删除结合中的元素,如果不用Iterator,在list循环中使用remove会抛异常。另外forEachRemaining()给出了简单的例子,里面的Consumer函数式接口有空再具体讲解。
1.3.2 Collection
集合层次结构中的根接口。集合(collection)表示一组对象,称为其元素。一些集合允许重复的元素,而另一些则不允许。一些是有序的,而其他则是无序的。 JDK不提供此接口的任何直接实现:它提供更特定的子接口(如Set和List)的实现。该接口通常用于传递集合,并在需要最大通用性的地方对它们进行操作。
Bag或多集(可能包含重复元素的无序集合)应直接实现此接口。所有通用的Collection实现类(通常通过其子接口之一间接实现Collection)都应提供两个“标准”构造函数:一个void(无参数)构造函数(用于创建一个空集合)和一个具有单个参数类型的构造函数集合,它将创建一个新集合,该集合具有与其参数相同的元素。实际上,后一个构造函数允许用户复制任何集合,从而生成所需实现类型的等效集合。没有强制执行此约定的方法(因为接口不能包含构造函数),但是Java平台库中的所有通用Collection实现都遵从。
如果此接口不支持该操作,则指定该接口中包含的“破坏性”方法(即修改其操作的集合的方法)以引发UnsupportedOperationException。在这种情况下,如果调用对集合没有影响,则这些方法可能会(但不是必需)引发UnsupportedOperationException。例如,如果要添加的集合为空,则对一个不可修改的集合调用addAll(Collection)方法可能(但并非必须)引发异常。
一些集合实现对它们可能包含的元素有限制。例如,某些实现禁止使用null元素,而某些实现对其元素类型进行限制。尝试添加不合格元素会引发未经检查的异常,通常为NullPointerException或ClassCastException。尝试查询不合格元素的存在可能会引发异常,或者可能仅返回false;否则,可能会返回false。一些实现将表现出前一种行为,而某些将表现出后者。更一般地,尝试对不合格元素进行操作,该操作的完成不会导致将不合格元素插入集合中,这可能会导致异常或成功实现,具体取决于实现方式。此类异常在此接口的规范中标记为“可选”。
由每个集合决定自己的同步策略。在实现没有更强有力的保证的情况下,未定义的行为可能是由于对另一个线程正在变异的集合上的任何方法的调用而导致的;这包括直接调用,将集合传递给可能执行调用的方法,以及使用现有的迭代器检查集合。
Collections Framework接口中的许多方法都是根据equals方法定义的。例如,contains(Object o)方法的规范说:“当且仅当此集合包含至少一个元素e使得(o == null?e == null:o.equals(e ))。”此规范不应解释为暗示调用带有非null参数o的Collection.contains会导致对任何元素e调用o.equals(e)。实现可以自由实现优化,从而避免了等号调用,例如,首先比较两个元素的哈希码。 (Object.hashCode()规范保证了具有不相等哈希码的两个对象不能相等。)更一般而言,各种Collections Framework接口的实现都可以自由地利用实现者认为合适的基础Object方法的指定行为。 。某些执行集合递归遍历的集合操作可能会失败,但自引用实例的例外情况是,集合直接或间接包含其自身。这包括clone(),equals(),hashCode()和toString()方法。实现可以有选择地处理自引用场景,但是大多数当前实现不这样做。
该接口是Java Collections Framework的成员。
源码部分
public interface Collection<E> extends Iterable<E> {
//值得一提的是:如果size超过Integer.MAX_VALUE也只会返回Integer.MAX_VALUE
int size();
boolean isEmpty();
//如果向集合里添加null,使用contains(null),也可以返回true
// 是否包含某个元素
boolean contains(Object o);
Iterator<E> iterator();
/**
* 深层拷贝,修改数组的数据不会对集合里的元素产生影响。
* 注意:只能返回Object[],不能强制转换其他类型,如需要转型,使用下面带泛型的方法。
*/
Object[] toArray();
<T> T[] toArray(T[] a);
// 加入元素e
boolean add(E e);
// 移除元素o
boolean remove(Object o);
// 是否包含集合c中的所有元素
boolean containsAll(Collection<?> c);
// 加入c集合里的元素
boolean addAll(Collection<? extends E> c);
// 移除c集合里的元素
boolean removeAll(Collection<?> c);
// 保留c集合里的元素
boolean retainAll(Collection<?> c);
// 清空集合
void clear();
// 集合中的某个值是否等于元素o
boolean equals(Object o);
// 如果a.equals(b),则hashCode()肯定相同,反之不一定
int hashCode();
// 针对parallelStream()添加的方法,用于分割集合,进行并行处理
// 因为对于数据源而言...集合是描述它最多的情况,所以Java已经默认在集合框架中为所有的数据结构提供了一个默认的Spliterator实现,相应的这个实现其实就是底层Stream如何并行遍历(Stream.isParallel())的实现啦,因此平常用到Spliterator的情况是不多的...因为Java8这次正是一次引用函数式编程的思想,你只需要告诉JDK你要做什么并行任务,关注业务本身,至于如何并行,怎么并行效率最高,就交给JDK自己去思考和优化速度了(想想以前写如何并发的代码被支配的恐惧吧)作为调用者我们只需要去关心一些filter,map,collect等业务操作即可
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
/**
* 如果满足filter,则删除,举个栗子:
* Collection<String> myHeart = new ArrayList<>();
* myHeart.add("Boduolaoshi");
* myHeart.add("GodnessY");
* System.out.println("before: " + myHeart.size());
* myHeart.removeIf(s -> s.equals("Boduolaoshi"));
* System.out.println("after: " + myHeart.size());
*/
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
// 创建一个基于Collection的stream
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//采用并行处理,使用多核cpu的特性
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
spliterator接口
这个接口有什么用呢(参考本文)
首先先直接给一个答案:Spliterator
(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator
,但一个是顺序遍历,一个是并行遍历
从最早Java提供顺序遍历迭代器Iterator
时,那个时候还是单核时代,但现在多核时代下,顺序遍历已经不能满足需求了...如何把多个任务分配到不同核上并行执行,才是能最大发挥多核的能力,所以Spliterator
应运而生啦
因为对于数据源而言...集合是描述它最多的情况,所以Java已经默认在集合框架中为所有的数据结构提供了一个默认的Spliterator
实现,相应的这个实现其实就是底层Stream
如何并行遍历(Stream.isParallel()
)的实现啦,因此平常用到Spliterator
的情况是不多的...因为Java8这次正是一次引用函数式编程的思想,你只需要告诉JDK你要做什么并行任务,关注业务本身,至于如何并行,怎么并行效率最高,就交给JDK自己去思考和优化速度了(想想以前写如何并发的代码被支配的恐惧吧)作为调用者我们只需要去关心一些filter
,map
,collect
等业务操作即可
所以想要看Spliterator
的实现,可以直接去看JDK对于集合框架的实现,很多实现类你可以在Spliterators
中找到的,也可以直接去你对应集合的stream方法中找到,比如ArrayList
点进去的是Collection的默认实现,只需要提供一个Spliterator
的实现,然后用StreamSupport
就可以构造一个Stream了,相当方便
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
对于Spliterator
接口的设计思想,应该要提到的是Java7的Fork/Join(分支/合并)框架,总得来说就是用递归的方式把并行的任务拆分成更小的子任务,然后把每个子任务的结果合并起来生成整体结果。带着这个理解来看看Spliterator
接口提供的方法
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
- 第一个方法
tryAdvance
就是顺序处理每个元素,类似Iterator
,如果还有元素要处理,则返回true,否则返回false - 第二个方法
trySplit
,这就是为Spliterator
专门设计的方法,区分与普通的Iterator
,该方法会把当前元素划分一部分出去创建一个新的Spliterator
作为返回,两个Spliterator
变会并行执行,如果元素个数小到无法划分则返回null - 第三个方法
estimateSize
,该方法用于估算还剩下多少个元素需要遍历 - 第四个方法
characteristics
,其实就是表示该Spliterator
有哪些特性,用于可以更好控制和优化Spliterator
的使用,具体属性你可以随便百度到,这里就不再赘言
例子就到这个链接去看吧
1.3.3 Map接口
源码部分
public interface Map<K,V> {
// 同样的,如果size超过Integer.MAX_VALUE也只会返回Integer.MAX_VALUE
int size();
// 该Map中是否没有键值对
boolean isEmpty();
// 如果此Map包含指定键的映射,则返回true。
boolean containsKey(Object key);
// 如果此Map将一个或多个键映射到指定值,则返回true
boolean containsValue(Object value);
// 返回指定键所映射到的值;如果此映射不包含键的映射关系,则返回null。
V get(Object key);
// 将指定值与该映射中的指定键相关联(可选操作)。如果该映射先前包含该键的映射,则将旧值替换为指定的值。 (仅当m.containsKey(k)返回true时,才认为映射m包含键k的映射。)
V put(K key, V value);
// 如果存在,则从此Map中删除键的映射(可选操作)。
// 返回此Map先前与该键相关联的值;如果该Map不包含该键的映射关系,则返回null。如果此映射允许空值,则返回值null不一定表示该映射不包含键的映射;否则,返回0。映射也可能将键显式映射为null。
// 调用返回后,该Map将不包含指定键的映射
V remove(Object key);
// 将所有映射从指定Map复制到此Map(可选操作)。
void putAll(Map<? extends K, ? extends V> m);
// 从此映射中删除所有映射(可选操作)。 此调用返回后,Map将为空。
void clear();
// 做了下面的这几个操作以后,可以使用不同的方法改变map的值
//对set的变动会影响到map,反过来也一样
Set<K> keySet();
//对Collection的变动会影响到map,反过来也一样
Collection<V> values();
//对Set的变动会影响到map,反过来也一样
Set<Map.Entry<K, V>> entrySet();
// 判断是否相等
boolean equals(Object o);
int hashCode();
//Entry start
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
//使用默认方法对Key进行比较
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey(){
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
//使用默认方法对Value比较
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
/**
* 自己传比较的方法,举个栗子:
* Map<String, Integer> map = new HashMap<>();
* map.put("sorted", 2);
* map.put("collect", 1);
* map.put("each", 3);
* System.out.println("before sort");
* map.entrySet().forEach(System.out::println);
* System.out.println("after sort");
* map.entrySet()
* .stream()
* .sorted(Map.Entry.comparingByKey((a, b) -> a.length() - b.length()))
* .collect(Collectors.toList()).forEach(System.out::println);
*/
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
}
//Entry end
//获取指定key 的value,没有则返回默认值
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
/**
* 对每队键值对操作: map.forEach((i, j) -> System.out.println(i + j))
* 注意这里的(i, j)的类型与你初始化map的键值类型对应,i即K, j即V
*/
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
/**
* 传入BiFunction类型,对每个键值对进行处理,返回类型与V类型相同
* Map<String, Integer> map = new HashMap<>();
* map.put("hi", 3);
* map.put("hello", 4);
* BiFunction<String, Integer, Integer> bi = (a, b) -> a.length() + b; //为了容易理解,这么写
* map.forEach((i, j) -> System.out.println(i + ":" + j));
* map.replaceAll(bi);
* map.forEach((i, j) -> System.out.println(i + ":" + j));
*/
// 用对该条目调用给定函数的结果替换每个条目的值,直到处理完所有条目或该函数引发异常为止
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}
// 如果指定的键尚未与值关联(或映射为null),则将其与给定值关联并返回null,否则返回当前值。
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
// 仅当当前映射到指定值时,才删除指定键的条目
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
// 仅当当前映射到指定值时,才替换指定键的条目
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}
// 仅当当前映射到一个值时,才替换指定键的条目
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
//如果key不存在,则通过mappingFunction生成value,并插入
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
//如果存在key对应的值,则通过remappingFunction来计算新的value,(value不为空)然后更新
//为空则删除key
default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
}
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
}
//将旧的oldValue和新的传进去value通过remappingFunction进行处理,然后更新
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
}
内部接口Entry
遍历方式
// 遍历方法测试
System.out.println("遍历方法测试:");
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
//第一种:普遍使用,二次取值(但是这种方式耗时应该比较多,因为get操作需要时间)
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时</span>
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
- 如果只是获取key,或者value,推荐使用keySet或者values方式;
- 如果同时需要key和value推荐使用entrySet;
- 如果需要在遍历过程中删除元素推荐使用Iterator;
- 如果需要在遍历过程中增加元素,可以新建一个临时map存放新增的元素,等遍历完毕,再把临时map放到原来的map中。