大家好!我是未来村村长,就是那个“请你跟我这样做,我就跟你这样做!”的村长????????!
||Data Structure||
未来村村长正推出一系列【Data Structure】文章,将从解读数据结构的角度上分析Java集合的源码。因为上的大多数描述java集合的文章,关注点在于其源码和方法,很少从对数据结构的讲解为切入点进行分析。以此为契机,未来村村长希望能从数据结构开始讲起,分析java集合是如何使用和如何实现的。
一、集合框架
1、集合总览
注:此图不全,实现关系不一定为上下级。
2、源码从接口读起
我们在此只看Collection和Map接口源码,目的是为了了解java集合规定了哪些基本方法,在此我们只需要简单知道有哪些方法即可。
(1)Collection
- boolean add(E e):向集合添加元素e,若指定集合元素改变了则返回true
- boolean addAll(Collection<? extends E> c):把集合C中的元素全部添加到集合中,若指定集合元素改变返回true
- void clear():清空所有集合元素
- boolean contains(Object o):判断指定集合是否包含对象o
- boolean containsAll(Collection<?> c):判断指定集合是否包含集合c的所有元素
- boolean isEmpty():判断指定集合的元素size是否为0
- boolean remove(Object o):删除集合中的元素对象o,若集合有多个o元素,则只会删除第一个元素
- boolean removeAll(Collection<?> c):删除指定集合包含集合c的元素
- boolean retainAll(Collection<?> c):从指定集合中保留包含集合c的元素,其他元素则删除
- int size():集合的元素个数
- T[] toArray(T[] a):将集合转换为T类型的数组
package java.util;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
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;
}
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
(2)Map
Map中的常用方法:
- void clear():删除该Map对象中所有键值对;
- boolean containsKey(Object key):查询Map中是否包含指定的key值;
- boolean containsValue(Object value):查询Map中是否包含一个或多个value;
- Set entrySet():返回map中包含的键值对所组成的Set集合,每个集合都是Map.Entry对象。
- Object get():返回指定key对应的value,如果不包含key则返回null;
- boolean isEmpty():查询该Map是否为空;
- Set keySet():返回Map中所有key组成的集合;
- Collection values():返回该Map里所有value组成的Collection。
- Object put(Object key,Object value):添加一个键值对,如果集合中的key重复,则覆盖原来的键值对;
- void putAll(Map m):将Map中的键值对复制到本Map中;
- Object remove(Object key):删除指定的key对应的键值对,并返回被删除键值对的value,如果不存在,则返回null;
- boolean remove(Object key,Object value):删除指定键值对,删除成功返回true;
- int size():返回该Map里的键值对个数;
内部类Entry,Map中包括一个内部类Entry,该类封装一个键值对,常用方法:
- Object getKey():返回该Entry里包含的key值;
- Object getvalue():返回该Entry里包含的value值;
- Object setValue(V value):设置该Entry里包含的value值,并设置新的value值。
package java.util;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.io.Serializable;
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
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());
}
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());
}
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());
}
}
boolean equals(Object o);
int hashCode();
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
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);
}
}
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);
}
}
}
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;
}
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;
}
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;
}
}
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;
}
}
看完源码,发现连接口都不简单,众所周知,java的接口只能定义静态且不可变的常量或者公共抽象方法,不可能定义非抽象的具体方法。但自从jdk1.8横空出世以后,它就被default关键字赋予了另一项很酷的能力——在接口中定义非抽象方法。我们可以通过下面这个例子,看其使用方式。
interface Interface404{
default int doSth(){
return 404;
}
}
public class IfaceTestNotFound implements Interface404{
public static void main(String[] args) {
new IfaceTestNotFound().do();
}
void do(){
System.out.println(Interface404.super.doSth());
}
}
二、迭代器
在Collection的代码中,我们可以看见Collection接口实现了Iterable接口,实现了该接口的类是可以通过迭代器进行遍历的,Java 中的 Collection 继承了 Iterable,List 和 Set 有继承了 Collection,所以 List 和 Set 中元素的遍历都是可以通过迭代器模式来实现。
1、迭代器的使用
在探究迭代器的实现以前,我们先来看看迭代器应该如何使用,以下这段代码来自菜鸟教程。
下面展示了遍历List的三种方式,一是通过For-Each方法,直接取出List中的元素。二是通过将ArrayList使用toArray转换为数组,通过for循环遍历。
import java.util.*;
public class Test{
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用 For-Each 遍历 List
for (String str : list) { //也可以改写 for(int i=0;i<list.size();i++) 这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为 for(String str:strArray) 这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
}
}
我们重点看第三种,通过list.iterator()创建了属于该list的迭代器,然后通过while循环,不断输出iterator的next元素。当然也可以用for循环结合list.size进行迭代器的使用。这有点像Scanner的hasnext()和nextInt()方法,不断的输入然后取到数据。
2、迭代器的实现
我们来看看Iterator的源码,探究迭代器的实现。
- 方法iterator()将返回一个Iterator。首次调用next()方法时,它将返回第一个元素
- next() 返回下一个元素
- hasNext() 检查集合中是否还有元素
- remove() 将迭代器新返回的元素删除
- forEachRemaining() 对每个剩余的元素执行指定的操作
package java.util;
import java.util.function.Consumer;
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
我们从较为简单的ArrayList的iterator去探究iterator的实现。在此要强调,java集合实现Iterable接口,所以拥有迭代器功能,但是每个集合对应的迭代器写法不同。源码较难理解,我们重点关注其hasnext()和next()就行。
public Iterator<E> iterator() {return new Itr();}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
我们可以看见next()最关键的两行代码
cursor = i + 1;
return (E) elementData[lastRet = i];
我们可以看见对源码对cursor的描述为index of next element to return(要返回的下一个元素的索引)。对lastRet的描述为返回的最后一个元素的索引,如果没有的话为-1(index of last element returned; -1 if no such)。每次返回数组元素后,将cursor指向下一个元素。这里要注意的是,cursor默认初始值为0,则返回的第一个元素为elementData[0],而不是从最后一个开始返回。之后会根据必要陆续推出其他集合类型的迭代器源码分析。