【 ArrayList 】

集合框架介绍

在这里插入图片描述

在这里插入图片描述

说明:对于以上的框架图有如下几点说明

  1. 所有集合类都位于 java.util 包下。Java 的集合类主要由两个接口派生而出:CollectionMapCollectionMapJava 集合框架的根接口,这两个接口又包含了一些子接口或实现类。
  2. 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
  3. 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
  4. 实现类:8个实现类(实线表示),对接口的具体实现。
  5. Collection 接口是一组允许重复的对象。
  6. Set 接口继承 Collection,集合元素不重复。
  7. List 接口继承 Collection,允许重复,维护元素插入顺序。
  8. Map 接口是键-值对象,与 Collection 接口没有什么关系。
  9. SetListMap 可以看做集合的三大类:
    List 集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
    Set 集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
    Map 集合中保存 Key-value 对形式的元素,访问时只能根据每项元素的 key 来访问其 value

0 准备知识

  • 集合底层采用数组方式
  • 怎么保证集合存放无限大小 - 数组扩容技术

数组扩容方式一:
Arrays.copyOf(objects, 10);

  • 参数一:需要扩容的数组对象
  • 参数二:设置新数组的长度
package com.snow.list;

import java.util.Arrays;

public class Test {

    public static void main(String[] args) {

         Object[] objects = { 1, 2 };
         System.out.println("扩容前数组长度 --- " + objects.length);
         for (Object o : objects) {
             System.out.println("扩容前数组元素 --- " + o);
         }
         System.out.println("################ 扩容后 #################");
         // 返回新的数组,将原来的数组的长度为2,现在扩容为10,原来本身的数据不变.
         Object[] copyNewObjects = Arrays.copyOf(objects, 10);
         System.out.println("扩容后数组长度 --- " + copyNewObjects.length);
         for (Object n : copyNewObjects) {
             System.out.println("扩容后数组元素 --- " + n);
         }

    }

}

控制台打印:

扩容前数组长度 --- 2
扩容前数组元素 --- 1
扩容前数组元素 --- 2
################ 扩容后 #################
扩容后数组长度 --- 10
扩容后数组元素 --- 1
扩容后数组元素 --- 2
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null

数组扩容方式二:
System.arraycopy(src, srcPos, dest, destPos, length);

  • 参数一:src 原数组
  • 参数二:srcPos 起始位置
  • 参数三:dest 目标数组
  • 参数四:destPos 目标起始位置
  • 参数五:length 复制长度
package com.snow.list;

public class Test {

    public static void main(String[] args) {

        int[] fun = { 0, 1, 2, 3, 4, 5, 6 };
        System.arraycopy(fun, 3, fun, 0, 2);
        // 3423456
        for (int i : fun) {
            System.out.print(i);
        }
    }

}

控制台打印:

3423456

说明:目标数组还是原来的数组,只是将原数组从索引为 3 的位置开始赋值到新数组中,没有赋值的还是原数组的值。

位算法:

package com.snow.list;

public class Test {

    public static void main(String[] args) {

        // 原来本身elementData容量大小 2
        int oldCapacity = 2;
        // 新数据容量大小 (oldCapacity >> 1)=oldCapacity/
        int newCapacity = oldCapacity + (oldCapacity >> 1);// (2+2/2)=3
        System.out.println(newCapacity);

    }

}

控制台打印:

3

1 JDK 数组源码说明

  1. JDK 1.7 之后 数组默认数据大小代码存放在 add 方法 (JDK 1.6 的时候 默认构造函数初始化 elementData 大小)

  2. Arraylist 底层采用数组实现 数组名称 elementData

  3. Arraylist 底层数组默认初始化为 10

2 List 框架

List 集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。

List 接口继承于 Collection 接口,它可以定义一个允许重复的有序集合。因为 List 中的元素是有序的,所以我们可以通过使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,这类似于 Java 的数组。

List 接口为 Collection 直接接口。List 所代表的是有序的 Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现 List 接口的集合主要有:

  • ArrayList
  • LinkedList
  • Vector
  • Stack

3 ArrayList 底层实现原理

Arraylist 底层基于数组实现

private Object[] elementData;

Arraylist 底层默认数组初始化大小为 10object 数组

public ExtArraylist() throws Exception {
	this(10);
}

public ExtArraylist(int initialCapacity) throws Exception {
	if (initialCapacity < 0) {
		throw new IllegalArgumentException("初始容量不能小于0 " + initialCapacity);
	}
	elementData = new Object[initialCapacity];
}

添加元素后大于当前数组的长度,则进行扩容,将数组的长度增加原来数组的一半。

// 增大数组空间
private void grow(int minCapacity) {
	// overflow-conscious code
	int oldCapacity = elementData.length;
	int newCapacity = oldCapacity + (oldCapacity >> 1); // 在原来容量的基础上加上
														// oldCapacity/2
	if (newCapacity - minCapacity < 0)
		newCapacity = minCapacity; // 最少保证容量和minCapacity一样
	if (newCapacity - MAX_ARRAY_SIZE > 0)
		newCapacity = hugeCapacity(minCapacity); // 最多不能超过最大容量
	// minCapacity is usually close to size, so this is a win:
	elementData = Arrays.copyOf(elementData, newCapacity);
}

4 手写 ArrayList

4.1 接口

package com.snow.list;

/**
 * 自定List接口<br>
 *
 * @param <E>
 */
public interface ExtList<E> {

    void add(E object);

    void add(int index, E object);

    Object remove(int index);

    boolean remove(E object);

    int getSize();

    Object get(int index);
}

4.2 实现类

package com.snow.list;

import java.util.Arrays;

/**
 * 纯手写ArrayList<br>
 *
 */
public class ExtArrayList<E> implements ExtList<E> {

    // 保存ArrayList中数据的数组
    private transient Object[] elementData;

    // ArrayList实际数量
    private int size;

    public ExtArrayList() {
        // 默认初始容量为10
        this(10);
    }

    public ExtArrayList(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        }
        // 初始化数组容量
        elementData = new Object[initialCapacity];
    }

    /**
     * 添加方法实现
     *
     * @param object
     */
    public void add(Object object) {
        ensureExplicitCapacity(size + 1);
        elementData[size++] = object;
    }

    /**
     * 添加方法实现
     *
     * @param object
     */
    public void add(int index, Object object) {
        rangeCheck(index);
        ensureExplicitCapacity(size + 1);
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = object;
        size++;
    }

    // 扩容
    private void ensureExplicitCapacity(int minCapacity) {
        // 如果存入的数据,超出了默认数组初始容量 就开始实现扩容
        if (size == elementData.length) {
            // 获取原来数组的长度 2
            int oldCapacity = elementData.length;
            // oldCapacity >> 1 理解成 oldCapacity/2 新数组的长度是原来长度1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1); // 3
            if (newCapacity < minCapacity) {
                // 最小容量比新容量要小的,则采用初始容量minCapacity
                newCapacity = minCapacity;
            }
            // System.out.println("oldCapacity:" + oldCapacity + ",newCapacity:"
            // + newCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }

    /**
     * 获取数据
     *
     * @param index
     * @return
     */
    public Object get(int index) {
        rangeCheck(index);
        return elementData[index];
    }

    /**
     * 删除数据
     *
     * @param index
     * @return
     */
    public Object remove(int index) {
        Object object = get(index);
        int numMoved = elementData.length - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        elementData[--size] = null;
        return object;
    }

    public boolean remove(E object) {
        for (int i = 0; i < elementData.length; i++) {
            Object element = elementData[i];
            if (element.equals(object)) {
                remove(i);
                return true;
            }
        }
        return false;
    }

    private void rangeCheck(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException("数组越界啦!");
        }
    }

    public int getSize() {
        return size;
    }

}

4.3 测试

package com.snow.list;

public class Test {

    public static void main(String[] args) {

        ExtArrayList extArrayList = new ExtArrayList(2);
        extArrayList.add("张三");
        extArrayList.add("李四");
        extArrayList.add("王武");
        for (int i=0; i<extArrayList.getSize(); i++) {
            System.out.println(extArrayList.get(i));
        }
    }

}

控制台打印:

张三
李四
王武

5 Vector 底层实现原理

Vector 是线程安全的,但是性能比 ArrayList 要低。

ArrayListVector 主要区别为以下几点:

  • Vector 是线程安全的,源码中有很多的 synchronized 可以看出,而 ArrayList 不是。导致 Vector 效率无法和 ArrayList 相比;
  • ArrayListVector 都采用线性连续存储空间,当存储空间不足的时候,ArrayList 默认增加为原来的50%,Vector 默认增加为原来的一倍;
  • Vector 可以设置 capacityIncrement,而 ArrayList 不可以,从字面理解就是 capacity 容量,Increment 增加,容量增长的参数。
private void grow(int minCapacity) {  
	 // overflow-conscious code  
	 int oldCapacity = elementData.length;  
	 int newCapacity = oldCapacity + (oldCapacity >> 1); //扩充的空间增加原来的50%(即是原来的1.5倍)  
	 if (newCapacity - minCapacity < 0) //如果容器扩容之后还是不够,那么干脆直接将minCapacity设为容器的大小  
	 	 newCapacity = minCapacity;  
	 if (newCapacity - MAX_ARRAY_SIZE > 0) //如果扩充的容器太大了的话,那么就执行hugeCapacity  
	 	 newCapacity = hugeCapacity(minCapacity);  
	 // minCapacity is usually close to size, so this is a win:  
	 elementData = Arrays.copyOf(elementData, newCapacity);  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不知所起 一往而深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值