数组和链表的实现
程序 = 算法 + 数据结构 这句话道出算法和数据结构的联系
(PS:虽然从知乎听来另一句话 程序 [ ] ≠ 软件,23333)
那么针对我们日常开发中,使用了别人封装好处理数据的类,是否有必要究其实现的原理,其实我觉得还是有必要的。因为在java中使用不同的集合类处理数据,虽然能达到同样的目的,但是性能方面却有所差异,同种效果的方法间的时间复杂度也不同(不知道时间复杂度是什么的同学点我)
现在来探讨下我们常用的ArrayList和LinkedList的实现吧:)
ArrayList
public class MyArrayList<E> implements Iterable<E>{
//默认长度
private static final int DEFAULT_SIZE =10;
//长度
private int thisSize;
//数组
private E [] es;
//构造器
public MyArrayList(){
clear();
}
//清除
private void clear() {
thisSize=0;
//让数字组恢复默认长度
returnDefaultSize(DEFAULT_SIZE);
}
//长度
public int size(){
return thisSize;
}
//是否为空
public boolean isEmpty(){
return thisSize==0;
}
//固定长度
public void trimToSize(){
returnDefaultSize(thisSize);
}
//获取第N个元素
public E get(int index){
if(index<0||index>=size())
throw new ArrayIndexOutOfBoundsException();
return es[index];
}
//设置第N个元素
public E set(int index,E e){
if(index<0||index>=size())
throw new ArrayIndexOutOfBoundsException();
//为了不破坏原来的数组而进行的操作
E old = es[index];
es[index] = e;
return old;
}
//固定长度
public void returnDefaultSize(int anyTypeLen) {
//如果目前容量大小 大于 输入的大小,无需扩展
if(thisSize > anyTypeLen)
return ;
//否则 扩展
E[] old = es;
es = (E[]) new Object[anyTypeLen];
for(int i=0;i<size();i++){
es[i] = old[i];
}
}
//添加元素
public boolean add(E e){
add(size(),e);
return true;
}
public boolean add(int index , E e){
if(es.length == size()){
//如果长度达到最长,扩容双倍再加一
returnDefaultSize(size() * 2 +1);
}
//如果当前的长度 大于 index的长度,就要执行循环,进行移位
for(int i=thisSize;i>index;i--){
es[i] = es[i-1];
}
//数组空出来的位置 用来装入要求插入的元素
es[index]=e;
thisSize++;
return true;
}
//删除元素
public E remove(int index){
if(index<0||index>size())
throw new ArrayIndexOutOfBoundsException();
//获取要删除的元素
E anyType = es[index];
//移位
for(int i=index;i<size()-1;i++){
es[index]=es[index+1];
}
thisSize--;
return anyType;
}
@Override
public Iterator<E> iterator() {
return new ArrayListItertor(this);
}
public class ArrayListItertor<E> implements Iterator<E>{
private int current=0;
private MyArrayList<E> myArrayList;
public ArrayListItertor(MyArrayList myArrayList){
this.myArrayList = myArrayList;
}
@Override
public boolean hasNext() {
return current < myArrayList.size();
}
@Override
public AnyType next() {
return myArrayList.es[current++];
}
@Override
public void remove() {
MyArrayList.this.remove(--current);
}
}
}
LinkedList 的实现
//实现LinkedList其实是实现一个双链表的过程
public class MyLinkedList<E> implements Iterable<E> {
private int theSize;
private int modCount = 0;
private Node<E> beginMarker;//开始 节点
private Node<E> endMarker;//结束 节点
private class Node<E>{
private E data;//元素
private Node<E> prev;//前驱
private Node<E> next;//后继
public Node(E e, Node<E> prev, Node<E> next) {
data = e;
this.prev = prev;
this.next = next;
}
}
public MyLinkedList(){
clear();
}
private void clear() {
beginMarker = new Node<>(null,null,null);
endMarker = new Node<>(null,beginMarker,null);
modCount++;
}
private int size(){
return theSize;
}
private boolean isEmpty(){
return size()==0;
}
public boolean add(E e){
add(size(),e);
return true;
}
private void add(int index, E e) {
addBefore(getNode(index),e);
}
public E get(int index){
return getNode(index).data;
}
//将原来的元素设置为现在的元素,并将原来的元素返回
public E set(int index,E e){
Node<E> p = getNode(index);
E oldAny =p.data;
p.data = e;
return oldAny;
}
private E remove(Node<E> p){
E element = p.data;
Node<E> next = p.next;
Node<E> prev = p.prev;
if(prev == null){
beginMarker = next;
}else{
prev.next = next;
p.prve = null;
}
if(next == null){
endMarker = prve;
}else{
next.prev = prev;
p.next = null;
}
p.data = null;
theSize -- ; //最总长度减一
modCount ++; //
return element;
}
//参数:原来的前驱几点,新的元素
//作用,插在队头
private void addBefore(Node<E> p, E e) {
Node<E> newNode = new Node<>(e,p.prev,p);
newNode.prev.next = newNode;
p.prev = newNode;
theSize ++;
modCount ++;
}
//拿到节点
private Node<E> getNode(int index) {
//开始的节点
Node<E> p;
if(index<0||index>size())
throw new IndexOutOfBoundsException();
//小于长度的二分之一,去队头查找
if(index<size()/2){
p = beginMarker.next;
for(int i=0;i<index;i++){
p = p.next;
}
//大于长度的二分之一,去队头查找
}else{
p = endMarker;
for(int i=size();i>index;i--){
p = p.prev;
}
}
return p;
}
//遍历
@Override
public Iterator<E> iterator() {
return new LinkedListIterator();
}
private class LinkedListIterator implements Iterator<E>{
private Node<E> current = (Node<E>) beginMarker.next;
private int expectedModCount = modCount;
//是否可以删除
private boolean okToRemove = false;
@Override
public boolean hasNext() {
return current != endMarker;
}
@Override
public E next() {
if(modCount !=expectedModCount)
throw new ConcurrentModificationException();
if(!hasNext())
throw new NoSuchElementException();
E nextItem = current.data;
current = (Node<E>) current.next;
okToRemove = true;
return current.data;
}
@Override
public void remove() {
if(modCount !=expectedModCount)
throw new ConcurrentModificationException();
if(!okToRemove)
throw new IllegalStateException();
MyLinkedList.this.remove(current.prev);
okToRemove = false;
expectedModCount++;
}
}
}
补充:
上面是ArrayList和LisnkedList的实现。其中,可能有部分读者发现,这两个我都去实现了Iterable 接口,并对方法Iterator 进行了修改。为什么要这么做?这要从Iterator 接口开始说起:
Iterator 迭代器
public interface Iterator{
//用与查找下一个元素是否存在
public boolean hasNext() {}
//拿到下一个元素
public Object next() {}
//移除下一个元素
public void remove() {}
}
Iterable 接口中包含了iterator方法。
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
这样一来,只要实现了Iterable 的类,就可以使用内部的增强for循环观察所有的项。
Iterator 中的remove方法,该方法只能删除由next返回的最新的项。也就是说,每当你想使用remove时,确保之前next方法有返回最新的项
正确的使用
next();
remove();
next();
remove();
错误的使用
next();
remove();
remove();//没有使用next方法,游标还是停留在上一个被删除项的位置
正因为如此,Iterator 的 remove的效率会比Collection里的remove的效率高。因为后者必须先确认要删除元素所在集合中的位置。
使用这种方式还有第二个原因:当Collection 在遍历时,结构被改变(add,remove,clear)时,这个集合的Iterator就不合法,会报错异常。如果我们是直接操作Iterator,使用它自身的remove,这样的迭代器仍然是合法的。