在集合Collection下的List中有两个实现使用的很频繁,一个是ArrayList,另一个是LinkedList,在学习中肯定都会有这样的疑问:什么时候适合使用ArrayList,什么时候用LinkedList?这时,我们就需要了解ArrayList和LinkedList的底层的实现,下面,为了更好的了解它们具体是怎样实现的,我们来写自己的ArrayList 和LinkedList。
ArrayList底层是基于数组实现的,数组在内存中是存储在连续的存储单元中,在数据查找的时候比较快,适用于不常进行插入数据和需要频繁的查找数据的操作,下面,我们将其实现(为了方便理解,不使用泛型,用Object存储数据):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) {
//要向一个数组中插入数据:1、数组的长度够不够 2、插入位置合不合法
//如果插入位置不合法,抛出索引越界异常
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;
//1、container指向一个新容器
container = new Object[size+1];
//将原数组的下标从0到index的元素复制到扩容后的容器中
System.arraycopy(temp, 0, container, 0, index);
//2、将index及其以后位置的数据整体向后移位
for(int i = size ; i > index ; i--) {
container[i] = temp[i-1];
}
//3、插入数据
container[index] = element;
//4、元素个数加一
++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
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 size) {
flag = false;
throw new ArrayIndexOutOfBoundsException("移除指定索引元素失败,索引值非法");
} else {
// 索引合法
for (int i = index; 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()。
写好该类后,对该类进行测试:
从实现结果看,我们基本实现了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 = 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
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 = 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 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;
}
}
写好代码后,对代码进行测试:
以上是个人拙见,如有错误,请指出,谢谢!