java集合框架总结以及源码分析(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangwei_620/article/details/82049502

一、集合框架总体架构图分析

1、首先我们先来看看一个集合框架的总图,有一个清晰的脉络机构,非常重要,因为不管我们学习那知识点,思路很重要。下面这张张图是我从网上博客摘取的,在此谢谢你精心的绘制。说明一下颜色含义:黄色:代表接口 绿色:代表抽象接口  蓝色:代表实现类

这是两个集合框架接口的总体图分为两个集合接口,分别是Collection接口,和map接口,collection集合是单列集合,而map集合是双列集合,下面我们将详细的从简单的功能功能学习到它的底层实现原理的探索。

二、Collection集合接口

1、首先说说集合与数组的区别

数组的长度是固定的,如果想增加长度,只能创建新的数组。

集合的长度是可变的,数据理论可以无限添加,自动扩容。

数组元素的类型必须是同一种类型,比如 String[]  arr = ["aaa","bbb"];

集合元素可以是不同的类型   ArrayList<Object> arr = new ArrayList<Object>();

2.集合框架的出现

是在jdk1.1时候只有一种集合Vector<XX>

后面在1.5的时候出现了集合框架

3、官方解释

  • 集合层次结构中的根界面。 集合表示一组被称为其元素的对象。 一些集合允许重复元素,而其他集合不允许。 有些被命令和其他无序。 JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如SetList 。 该界面通常用于传递集合,并在需要最大的通用性的情况下对其进行操作。

  • 多重集 (可能包含重复元素的无序集合)应直接实现此接口。

4、Collection的实现接口以及子实现类,功能以及特点

集合的顶层接口:Collection<E>

下面主要有:三个接口分别是

List                Set                     Queue

List列表是有序的,有下标的,可重复的

Set集合是无序的,但是要把LinkHashSet除外,没有下表,不可重复

List的主要实现类是:ArrayList    LinkedList      Vector  

ArrayList底层是通过数组实现的

LinkedList底层是通过链表实现的

Vector底层是通过

Set的实现类是:Hash Set     LinkedHashSet        TreeSet     

Hash Set底层是通过哈希表实现的

LinkedHashSet是通过链表+哈希表实现的,它也是一种链式哈希级

TreeSet底层是通过树结构实现的

5.Collection的一些通用方法

增:public boolean add(E e);

删:public void remove(E e);

改:无

查:无

其他方法:

public void  clear();//清空集合中的元素

public  int  size();获取集合的长度(元素的个数);

public  boolean  contains(E e);//判断当前集合中是否包含指定对象

public boolean  isEmpty();//判断集合是否为元素

public  Object[]  toArray();//将集合元素转换为数组

containsAll(Collection c)://是否包含集合c中的所有元素

iterator()://返回Iterator对象,用于遍历集合中的元素

remove(Object o)://移除元素

removeAll(Collection c)://相当于减集合

6.迭代器的实现原理

我们根据第一个图可以看出Collection接口上面扩展了Iterator接口,那么我们就可以知道了,所有Collection接口下面的实现类,我们都可以通过迭代器去遍历集合中的元素,当然用增强for也可以,因为增强for的底层也是通过迭代器的原理实现的。

我们先看看它的代码写法

Iterator<集合泛型>  it = new Iterator();
while(it.hasNext()){
    集合泛型 next   = it.next();
     System.out.println(next);
}

迭代器的原理图:

通过hasNext()指针判断集合中是否有写一个元素,如果有通过next()访问集合元素,然后输出。注意如果访问的是ArrayList集合则是按照一定的顺序输出的,访问set集合则是通过按照访问的随机顺。

主要这个非常重要:在使用迭代器的时候,一定不能改变集合的长度(增加或者删除),每次迭代器都会判断底层的记忆长度是否与实际长度相等,如果不相等,则会出现一个并发修改异常,ConcarrentModifiCationException,可以在集合中修改元素,但不能改变长度。

三、一起探究Collection底层源码

首先我们写一个Demo,通过多态的形式,创建一个ArrayList()类分析

import java.util.ArrayList;
import java.util.Collection;

public class Demo1_Collecton {
    public static void main(String[] args) {
        Collection<String> arr = new ArrayList<String>();
    }
}

通过按住Ctrl,鼠标点击Collection我们就可以进入源码

* @param <E> the type of elements in this collection
 *
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @see     Set
 * @see     List
 * @see     Map
 * @see     SortedSet
 * @see     SortedMap
 * @see     HashSet
 * @see     TreeSet
 * @see     ArrayList
 * @see     LinkedList
 * @see     Vector
 * @see     Collections
 * @see     Arrays
 * @see     AbstractCollection
 * @since 1.2
 */

public interface Collection<E> extends Iterable<E> {

我们可以Collection接口有好多类都简介或者直接实现了Collection,可以看出它是继承了Iterable这个接口,那么在这个Collection源码中下面就是一下我们前面所列举的一些方法,我们可以看看

 // Query Operations
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();
// Comparison and hashing
boolean equals(Object o);
int hashCode();
@Override
    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);
    }

这些大多都是抽象接口。上面的那些几本方法就不说了,自己了解一下,然后到比较哈希码这了,我们说说如何比较一个元素是否相等,我们一般有步骤是:

1.通过hash码比较,如果hash码值相等

2.通过equals方法比较,如果元素内容相等,则是同一个元素,反之不是同一个元素

注意:

我们经常在学习java的过程中从内存角度分析时:每次在创建一个对象,我们就说它有了一个地址值,我们还可以打印出这个地址值,但是这个地址值是假的,骗人的,实际上是这个新创建对象的哈希码值,哈希值是二进制的,把哈希值转换为十六进制数显示在控制台上,让你误以为是真正的地址是值。

其实Java中确实有真正的地址值,确实存在堆中,只是我们不能打印出来而已,所以给你一个哈希值转换的十六进制给你看。

我们继续探究Collection继承的Iterable()接口中的内容

/**
 * Implementing this interface allows an object to be the target of the enhanced
 * {@code for} statement (sometimes called the "for-each loop" statement).
 *
 * @param <T> the type of elements returned by the iterator
 *
 * @since 1.5
 * @jls 14.14.2 The enhanced {@code for} statement
 */
public interface Iterable<T> {

里面也是一个接口,第一段英文说,实现这个接口可以让你把对象当作一个目标,也就是说你可以对你集合中的元素进行操作。

我们看看有哪些方法

 /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();

返回一个迭代器<T>代表是这个迭代器的泛型

default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

我们看一这个代码是增强for循环针对元素进行遍历,首先要通过Objects工具类判断actionshif为空,如果不为空,然后就开始遍历,如果为空就会抛出一个NullPointException的异常

我们可以点进去看看一个工具类的判断方法

 public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

通过对传入的对象进行为空判断,为空则抛出异常,反之返回这个对象继续往下进行

forEach中通过Consumer接口传入参数进行处理,不做任何返回值,这个是接口是在jdk1.8时候出现的

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }

注意里面的两个方法:

1.accept()方法是用于处理输入的参数

2.andThen()用于链式处理,通过朗姆大表达式返回的,也是在jdk8出现的新特性.

我在在返回到Collection源码界面,分析一下

default Spliterator<E> spliterator()

我们点进去看看

* If the boolean system property {@code org.openjdk.java.util.stream.tripwire}
 * is set to {@code true} then diagnostic warnings are reported if boxing of
 * primitive values occur when operating on primitive subtype specializations.
 *
 * @param <T> the type of elements returned by this Spliterator
 *
 * @see Collection
 * @since 1.8
 */
public interface Spliterator<T> {

这个接口是在jdk1.8的时候出现的,Spliterator是一个可分割迭代器,它出现的目的是什么呢,

从最早Java提供顺序遍历迭代器Iterator时,那个时候还是单核时代,但现在多核时代下,顺序遍历已经不能满足需求了...如何把多个任务分配到不同核上并行执行,才是能最大发挥多核的能力,因为对于数据源而言...集合是描述它最多的情况,所以Java已经默认在集合框架中为所有的数据结构提供了一个默认的Spliterator实现,相应的这个实现其实就是底层Stream如何并行遍历(Stream.isParallel())的实现啦,因此平常用到Spliterator的情况是不多的...因为Java8这次正是一次引用函数式编程的思想,你只需要告诉JDK你要做什么并行任务,关注业务本身,至于如何并行,怎么并行效率最高,就交给JDK自己去思考和优化速度了.

Collection的源码我们就先分析到这,下一篇我们将对它的子类实现进行功能介绍和源码分析(List和Set两种集合)

展开阅读全文

没有更多推荐了,返回首页