在集合Collection下的List中有两个实现使用的很频繁,一个是ArrayList,另外一个是LinkedList,在学习中确定都会有这样的疑问:何时适合使用ArrayList,何时用LinkedList?这时,咱们就须要了解ArrayList和LinkedList的底层的实现,下面,为了更好的了解它们具体是怎样实现的,咱们来写本身的ArrayList 和LinkedList。java
ArrayList底层是基于数组实现的,数组在内存中是存储在连续的存储单元中,在数据查找的时候比较快,适用于不常进行插入数据和须要频繁的查找数据的操做,下面,咱们将其实现(为了方便理解,不使用泛型,用Object存储数据):
node
import java.util.Arrays; /** * 顺序存储结构:类ArrayList的实现 * @author liuzb * 2017年8月8日 下午2:58:59 */ public class SequenceStroeLinearList{ /** * 用于存储容器中实际存储的元素个数 */ private int size = 0; /** * 底层用于存储数据的容器 */ private Object[] container; /** * 在顺序存储结构中,用于初始化 */ private static final Object[] EMPTY_CONTAINER = {}; /** * 构造器,用于初始化存储数据的容器 */ public SequenceStroeLinearList(){ this.container = EMPTY_CONTAINER; } /** * 构造器,用于初始化存储数据的容器 */ public SequenceStroeLinearList(int minCapactiy) { if(minCapactiy > 0) { container = new Object[minCapactiy]; }else if(minCapactiy == 0) { container = EMPTY_CONTAINER; }else { throw new ArrayIndexOutOfBoundsException(); } } /** * 向容器中添加一个元素 * @param index 添加元素位置 * @param element 待添加元素 */ public void insert(int index ,Object element) { //要向一个数组中插入数据:一、数组的长度够不够 二、插入位置合不合法 //若是插入位置不合法,抛出索引越界异常 if(index > container.length || index <0) { throw new ArrayIndexOutOfBoundsException(); }else { //插入位置合法 //若是是在尾部插入数据 if(index == container.length) { container = Arrays.copyOf(container, ++size); container[index] = element; }else { //若是不是在尾部插入数据,先用临时变量存储容器中的内容 Object[] temp = container; //一、container指向一个新容器 container = new Object[size+1]; //将原数组的下标从0到index的元素复制到扩容后的容器中 System.arraycopy(temp, 0, container, 0, index); //二、将index及其之后位置的数据总体向后移位 for(int i = size ; i > index ; i--) { container[i] = temp[i-1]; } //三、插入数据 container[index] = element; //四、元素个数加一 ++size; } } } /** * 向容器中添加数据 * @param obj 须要添加的对象 */ public void add(Object obj) { insert(size,obj); } /** * 容器中实际存储的元素个数 * @return */ public int size() { return size; } /** * 获取指定索引位置的对象 * @param index 指定位置的索引 * @return 指定位置的对象 */ public Object get(int index) { if(index <0 || index > size) { throw new ArrayIndexOutOfBoundsException(); } return container[index]; } /** * 获取指定对象的索引 * @param obj 须要获取索引的对象 * @return 索引 */ public int indexOf(Object obj) { int index = -1; for(int i = 0; i<size ; i++) { if(container[i].equals(obj)) { index = i; } } return index; } /** * 容器中是否包含某个元素 * @param obj * @return false 不包含 true 包含 */ public boolean contains(Object obj) { return indexOf(obj) == -1 ? false :true; } /** * 从容器中移除指定索引的元素 * @param index 须要移除元素的索引 * @return 移除 */ public boolean remove(Integer index) { boolean flag = true; // 非法索引,抛出异常 if (index < 0 || index > size) { flag = false; throw new ArrayIndexOutOfBoundsException("移除指定索引元素失败,索引值非法"); } else { // 索引合法 for (int i = index; i < size-1; i++) { //将index到size的元素依次往前移位 container[i] = container[i + 1]; } // 将末尾元素值赋为 null container[size-1] = null; // 元素个数减一 -- size; } return flag; } /** * 移除指定元素 * @param obj 须要移除的元素 * @return true :移除成功 false:移除失败 */ public boolean remove(Object obj){ if(contains(obj)) { remove(indexOf(obj)); return true; } return false; } }
在实现中,用到了util包下面的Arrays帮助类,此处也可使用System.arrayCpy()。数组
写好该类后,对该类进行测试:
ide
从实现结果看,咱们基本实现了ArrayList的功能,此处重要的两个方法是插入数据和移除数据,固然本程序也有bug,就是remove方法。学习
LinkedList底层是基于链表实现的,在数据插入和删除时速度较快,适用于频繁进行插入和删除的操做,这里咱们实现一个单链表:
测试
/** * 自定义链式存储列表 :单链表 * @author liuzb * 2017年8月9日 上午11:24:07 */ public class MySingleLinkedList { /** * 单链表中的首节点 */ private Node header; /** * 单链表中的尾节点 */ private Node tail; /** * 单链表中实际存储元素的个数 */ private int size; /** * 内部类,用于封装节点须要的数据和下一个节点的地址 * @author liuzb * 2017年8月9日 上午11:24:43 */ private class Node{ /** * 当前链表存储的数据 */ private Object data; /** * 当前节点存储的下一个节点的地址 */ private Node next; /** * 无参构造器,用于节点的初始化 */ public Node() { } /** * 有参构造器,用于节点的初始化 * @param date 节点存储的值 * @param next 节点中保存的下一个节点的地址 */ public Node(Object data,Node next) { this.data = data; this.next = next; } /** * 获取当前节点的存储的值 * @return 节点值 */ public Object getData() { return data; } } /** * 单链表头插入法 * @param item 须要存储的数据 */ public void addHeader(Object item) { //定义一个节点 Node node = null; //若是原链表是空表 if(size == 0) { //构建一个新的节点,节点的下一个节点指向null node = new Node(item,null); //头结点和尾节点都指向新节点 header = node; tail = header; }else { //若是原链表不是空表,定义一个新节点,新节点的下一个节点指向原来的头结点 node = new Node(item,header); //新节点变成了头结点 header = node; } //元素的个数加一 size ++; } /** * 单链表为插入法 * @param item 须要出入的元素 */ public void addLast(Object item) { //建立一个新节点 Node node = new Node(item,null); // 若是原来的链表是空表 if(size == 0) { //单链表的头结点和尾节点都指向新节点 tail = header = node; }else { //原来的尾节点的下一个节点指向新节点 tail.next = node; //新节点变成了尾节点 tail = node; } //链表的元素个数加一 size ++; } /** * 向单链表中添加元素 * @param item 待添加的元素 * @return */ public void add(Object item) { //方法中默认使用尾插入法插入元素 addLast(item); } /** * 移除指定位置的元素 * @param index 须要移除元素的索引 * @return true:移除成功 false:移除失败 */ public boolean remove(int index) { boolean flag = false; //若是索引非法,抛出异常 if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("移除元素的索引越界"); } else { //若是移除的是头结点 if (index == 0) { //原头结点的下一个节点变成了头结点 header = header.next; }else if(index == size-1) { //若是删除的是尾节点,先获取原尾节点的前一个节点 Node node = getNodeByIndex(index-1); //将原尾节点的前一个节点存储的下一个节点地址信息置为null node.next = null; //原尾节点的前一个节点变成了尾节点 tail = node; }else { //删除的既不是头结点,也不是尾节点,将须要删除的数据先暂时存储 Node removeNode = getNodeByIndex(index); //获取须要删除数据的前一个节点 Node node = getNodeByIndex(index - 1); //将前一个节点的下一个节点指向须要删除的节点的下一个节点 node.next = removeNode.next; } //元素个数减一 size -- ; flag = true; } return flag; } /** * 获取指定索引的节点 * @param index 须要获取的节点的索引值 * @return 节点对象 */ public Node getNodeByIndex(int index) { Node current = header; for(int i = 0;i < size && current != null;i++) { if(index == i) { return current; } current = current.next; } return null; } /** * 获取单链表中元素个数 * @return 元素个数 */ public int size() { return size; } /** * 获取指定索引的节点值 * @param index 须要获取的节点的索引 * @return 节点值 */ public Object get(int index) { //索引非法,抛出异常 if(index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException("没法获取该节点的值"); } return getNodeByIndex(index).getData(); } /** * 向链表指定位置插入数据 * @param index 待插入位置 * @param item 待插入数据 * @return true 插入成功 false 插入失败 */ public boolean insert(int index,Object item) { boolean flag = true; if(index < 0 || index >size) { flag = false; throw new ArrayIndexOutOfBoundsException(); } //若是插入到头结点前 if(index == 0) { header = new Node(item,header); }else if(index == size) { //插入到尾节点后 //定义一个新的节点 Node node = new Node(item,null); //尾节点的下一个节点指向新节点 tail.next = node; //新节点变成了尾节点 tail = node; }else { //在首节点和尾节点之间插入节点 //获取出入位置的节点 Node indexNode = getNodeByIndex(index); //定义新节点,新节点的下一个节点指向原插入位置的节点 Node newNode = new Node(item,indexNode); //获取插入位置的前一个节点 Node node = getNodeByIndex(index-1); //插入位置的前一个节点的下一个节点指向新节点 node.next = newNode; } //元素个数加一 size ++ ; return flag; } }
写好代码后,对代码进行测试:this
以上是我的拙见,若有错误,请指出,谢谢!对象