数据结构与算法——数组

1、概念

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

2、特点

2.1、线性表

​ 线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。其实除了数组,链表、队列、栈等也是线性表结构。

2.2、内存空间连续,数据类型相同

=+当前元素地址=数组首地址+元素字节大小\ast数组下标

2.3、高效的随机访问,低效的插入和删除

由于内存空间连续,访问可以通过一次寻址找到对应数据,而插入和删除后需要将后续元素进行多次数据搬移

访问的时间复杂度:O(1)

插入和删除的时间复杂度:O(n)

3、数组和容器对比

3.1、容器优势

封装了很多数组操作的方法,便于开发

支持动态扩容

3.2、数组优势

可以存储基本类型

表示多维数组更加直观

4、其他

4.1、数组下标为何从零开始

为了方便随机访问(计算元素内存地址),如果下标从1开始会多做一次减法操作

4.2、手撕数组

package com.company;

import java.util.Arrays;

/**
 * 功能描述:
 *
 * @author liuchaoyong
 * @version 1.0
 * @date 2020/1/9 11:18
 */
public class MyArrayList<E> {

    /**
     * 空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认长度的空数组,用于区分当前ArrayList是默认构造函数创建的,还是有参构造函数创建的,
     * 主要是为了提升扩容效率
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 数组默认大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 缓存数组
     */
    transient Object[] elementData;

    /**
     * 数组最大长度(由于数组是对象,包含对象头信息,不超过8个字节,
     * 所以数组实际占用的空间要比实际大一点,故-8)
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * ArrayList大小,注意是elementData中实际存在的元素个数,
     * 而不是elementData的实际长度
     */
    private int size;

    /**
     * 无参构造函数会创建一个默认长度的空数组
     */
    public MyArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 此有参构造函数创建一个指定长度的数组
     * >0创建指定长度的数组
     * =0指向空数组
     * <0抛出异常
     */
    public MyArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        }
    }

    /**
     * 获取指定下标元素
     */
    public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

    /**
     * 替换指定下标元素,并返回旧元素
     */
    public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    /**
     * 向数组末尾添加元素
     */
    public boolean add(E element) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = element;
        return true;
    }

    /**
     * 向指定下标添加元素
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);
        //将index及其后面位置的元素整体从index+1开始覆盖
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * 移除指定下标元素并返回
     */
    public E remove(int index) {
        rangeCheck(index);
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        //将index+1及其后面位置的元素整体从index开始覆盖,并将末尾元素置空触发gc回收
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
        return oldValue;
    }

    /**
     * 移除某个元素
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断数组中是否包含某元素
     */
    public boolean contains (Object o){
        return indexOf(o) >= 0;
    }

    /**
     * 获取数组中第一次出现某元素的下标
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    return index;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (o.equals(elementData[index])) {
                    return index;
                }
            }
        }
        return -1;
    }

    /**
     * 获取数组中最后一次出现某元素的下标
     */
    public int lastIndexOf(Object o){
        if (o == null) {
            for (int index = size-1; index > 0; index--) {
                if (elementData[index] == null) {
                    return index;
                }
            }
        } else {
            for (int index = size-1; index > 0; index--) {
                if (o.equals(elementData[index])) {
                    return index;
                }
            }
        }
        return -1;
    }

    /**
     * 获取ArrayList大小
     */
    public int size() {
        return size;
    }

    /**
     * 判断ArrayList是否为空
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 清空数组
     */
    public void clear() {
        for (int index = 0; index < size; index++) {
            elementData[index] = null;
        }
        size = 0;
    }

    /**
     * 数组越界检查
     */
    private void rangeCheck(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 数组越界检查
     */
    private void rangeCheckForAdd(int index) {
        //rangeCheck没有
        //System.arraycopy()有性能消耗,在此拦截index<0防止异常输入无谓地消耗性能
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 获取指定下标元素
     */
    @SuppressWarnings("unchecked")
    private E elementData(int index) {
        return (E) elementData[index];
    }

    /**
     * 快速移除指定下标元素,没有越界校验,也没有返回值
     */
    private void fastRemove(int index) {
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
    }

    /**
     * 扩容校验
     */
    private void ensureCapacityInternal(int minCapacity) {
        if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);
        }
        if (minCapacity - elementData.length > 0) {
            grow(minCapacity);
        }

    }

    /**
     * 缓存数组扩容
     */
    private void grow(int minCapacity) {
        //旧数组长度
        int oldCapacity = elementData.length;
        //扩容1.5倍的新数组长度(要说为啥计算不乘1.5,而用移位,因为从硬件上看移位更容易操作更快呀)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果旧数组长度足够大,计算出的新数组长度有可能发生整型溢出变为负数,做一下数据修正
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        //如果旧数组长度足够大,新数组长度超出上限,则给新数组申请最大长度
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (minCapacity < 0) {
                throw new OutOfMemoryError();
            }
            //如果数组需要的长度比最大限制还要大,则尝试申请Integer.MAX_VALUE长度的数组(失败会报内存溢出),否则把最大限制长度赋给新数组
            newCapacity = minCapacity > MAX_ARRAY_SIZE ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        }
        Object[] copy = new Object[newCapacity];
        System.arraycopy(elementData, 0, copy, 0,
                elementData.length);
        elementData = copy;
    }

}
发布了61 篇原创文章 · 获赞 13 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章