一、链表和数组

图2解释的为单链表
应用:List:
- ArrayList:底层实现是数组,查询快,增删慢
- LinkedList:底层实现是双链表链表,查询慢,增删快
二、自定义类实现ArrayList与LinkedList
1、MyArrayList的实现(java代码)
public class MyArrayList<AnyType> implements Iterable<AnyType> {
//默认大小
private static final int DEFAULT_CAPACITY = 10;
//底层用数组实现,因为可以存放任何类型,所以使用泛型
private AnyType[] theItems;
//实际存放的元素个数
private int theSize;
//初始化
public MyArrayList( ){
doClear( );
}
//返回的实际元素个数
public int size( ){
return theSize;
}
//判断是否为空
public boolean isEmpty( ){
return size( ) == 0;
}
//返回指定索引位置的元素
public AnyType get( int idx ){
if( idx < 0 || idx >= size( ) )
throw new ArrayIndexOutOfBoundsException( "Index " + idx + "; size " + size( ) );
return theItems[ idx ];
}
//改变已有元素的值
public AnyType set( int idx, AnyType newVal ){
if( idx < 0 || idx >= size( ) )
throw new ArrayIndexOutOfBoundsException( "Index " + idx + "; size " + size( ) );
AnyType old = theItems[ idx ];
theItems[ idx ] = newVal;
return old;
}
//动态扩容
public void ensureCapacity( int newCapacity ){
if( newCapacity < theSize )
return;
AnyType [ ] old = theItems;
//因为创建泛型类型的数组是非法的,所以先创建一个Object类型的数组然后再强转成泛型类型的数组
theItems = (AnyType []) new Object[ newCapacity ];
//将旧数组中的元素复制到新数组中
for( int i = 0; i < size( ); i++ )
theItems[ i ] = old[ i ];
}
//向末尾添加元素
public boolean add( AnyType x ){
add( size( ), x );
return true;
}
//在指定位置添加元素,
public void add( int idx, AnyType x ){
//如果没有空间了,就将空间扩展为原来的两倍
if( theItems.length == size( ) )
ensureCapacity( size( ) * 2 + 1 );
//从插入索引位置开始,将元素向后挪一位,为新元素腾位置
//注意要从后往前移动位置,否则前面元素的值会覆盖后面元素的值
for( int i = theSize; i > idx; i-- )
theItems[ i ] = theItems[ i - 1 ];
//将新元素插到指定位置
theItems[ idx ] = x;
//元素个数加1
theSize++;
}
//移除指定位置的元素
public AnyType remove( int idx ){
AnyType removedItem = theItems[ idx ];
//将idx索引后的元素依次向前移动一个位置
for( int i = idx; i < size( ) - 1; i++ )
theItems[ i ] = theItems[ i + 1 ];
//元素个数-1
theSize--;
return removedItem;
}
public void clear( ){
doClear( );
}
//还原初始设置
private void doClear( ){
theSize = 0;
ensureCapacity( DEFAULT_CAPACITY );
}
//返回迭代器的实例
public java.util.Iterator<AnyType> iterator( ){
return new ArrayListIterator( );
}
//重写toString方法
public String toString( ){
StringBuilder sb = new StringBuilder( "[ " );
for( AnyType x : this )
sb.append( x + " " );
sb.append( "]" );
return new String( sb );
}
//迭代器的内部类
private class ArrayListIterator implements java.util.Iterator<AnyType>{
//相当于指针
private int current = 0;
//判断是否可以删除(并未做完整判断)
private boolean okToRemove = false;
//判断是否还有元素
public boolean hasNext( ){
return current < size( );
}
//返回下一个元素
public AnyType next( ){
if( !hasNext( ) )
throw new java.util.NoSuchElementException( );
okToRemove = true;
return theItems[ current++ ];
}
//删除元素
public void remove( ){
if( !okToRemove )
throw new IllegalStateException( );
//由于next方法,所以指针已经指向下一个元素,所以--current实际是删除当前元素,
//而后面的元素也会向前挪一个位置
MyArrayList.this.remove( --current );
okToRemove = false;
}
}
}
//测试类
class TestArrayList{
public static void main( String [ ] args ){
MyArrayList<Integer> lst = new MyArrayList<>( );
for( int i = 0; i < 10; i++ )
lst.add( i );
for( int i = 20; i < 30; i++ )
lst.add( 0, i );
lst.remove( 0 );
lst.remove( lst.size( ) - 1 );
System.out.println( lst );
}
}
2、MyLinkedList的实现(java代码)
虽然说LinkedList增删快,但其实是增删两头的元素比较快,如果是增删中间的元素,因为要查找指定的元素,所以效率也会受到影响
LinkedList的是由双向链表实现的

public class MyLinkedList<AnyType> implements Iterable<AnyType> {
//私有嵌套双向链表Node类
private static class Node<AnyType>{
//节点中存放的数据
public AnyType data;
//当前节点的上一个节点
public Node<AnyType> prev;
//当前节点的下一个节点
public Node<AnyType> next;
public Node( AnyType d, Node<AnyType> p, Node<AnyType> n ){
data = d; prev = p; next = n;
}
}
//元素个数
private int theSize;
//modeCount代表自从构造以来对链表所做改变的次数,每次对add或remove的调用都将更新modeCount
//当一个迭代器被建立时,他将存储集合modCount到expectedModeCount
//每次对一个迭代器方法(next或remove)的调用都将检查modeCount是否等于expectedModeCount
//当两个数不匹配时将抛出ConcurrentModificationException
private int modCount = 0;
//头节点和尾节点的引用主要是为了通过排除许多特殊情形来简化代码。
//例如如果不用头节点,则删除第一个节点则变成特殊情况,必须重新调整第一个节点的链
private Node<AnyType> beginMarker;
private Node<AnyType> endMarker;
public MyLinkedList( ){
doClear( );
}
private void clear( ){
doClear( );
}
//初始化,尾节点的上一个元素指向头节点,头节点指向尾节点
public void doClear( ){
beginMarker = new Node<>( null, null, null );
endMarker = new Node<>( null, beginMarker, null );
beginMarker.next = endMarker;
theSize = 0;
modCount++;
}
public int size( ){
return theSize;
}
public boolean isEmpty( ){
return size( ) == 0;
}
//在链表最后添加元素
public boolean add( AnyType x ){
add( size( ), x );
return true;
}
//在指定位置添加元素
public void add( int idx, AnyType x ){
addBefore( getNode( idx, 0, size( ) ), x );
}
//向执行节点前加入新节点
private void addBefore( Node<AnyType> p, AnyType x ){
Node<AnyType> newNode = new Node<>( x, p.prev, p );
newNode.prev.next = newNode;
p.prev = newNode;
theSize++;
modCount++;
}
//获取指定位置的节点的数据
public AnyType get( int idx ){
return getNode( idx ).data;
}
//更新已有节点
public AnyType set( int idx, AnyType newVal ){
Node<AnyType> p = getNode( idx );
AnyType oldVal = p.data;
p.data = newVal;
return oldVal;
}
private Node<AnyType> getNode( int idx ){
return getNode( idx, 0, size( ) - 1 );
}
//在特定范围内查找指定索引的节点
private Node<AnyType> getNode( int idx, int lower, int upper ){
Node<AnyType> p;
if( idx < lower || idx > upper )
throw new IndexOutOfBoundsException( "getNode index: " + idx + "; size: " + size( ) );
//要查找的索引在前半部分,则从0开始遍历到要查找的索引
if( idx < size( ) / 2 ){
p = beginMarker.next;
for( int i = 0; i < idx; i++ )
p = p.next;
}else{
//要查找的索引在后半部分,则从最后面开始查找,查找到指定指定索引
p = endMarker;
for( int i = size( ); i > idx; i-- )
p = p.prev;
}
return p;
}
public AnyType remove( int idx ){
return remove( getNode( idx ) );
}
private AnyType remove( Node<AnyType> p ){
p.next.prev = p.prev;
p.prev.next = p.next;
theSize--;
modCount++;
return p.data;
}
public String toString( ){
StringBuilder sb = new StringBuilder( "[ " );
for( AnyType x : this )
sb.append( x + " " );
sb.append( "]" );
return new String( sb );
}
//获得集合的迭代器对象
public java.util.Iterator<AnyType> iterator( ){
return new LinkedListIterator( );
}
//链表迭代器的类
private class LinkedListIterator implements java.util.Iterator<AnyType>{
private Node<AnyType> current = beginMarker.next;
private int expectedModCount = modCount;
private boolean okToRemove = false;
public boolean hasNext( ){
return current != endMarker;
}
public AnyType next( ){
if( modCount != expectedModCount )
throw new java.util.ConcurrentModificationException( );
if( !hasNext( ) )
throw new java.util.NoSuchElementException( );
AnyType nextItem = current.data;
current = current.next;
okToRemove = true;
return nextItem;
}
public void remove( ){
if( modCount != expectedModCount )
throw new java.util.ConcurrentModificationException( );
if( !okToRemove )
throw new IllegalStateException( );
//之前在调用it.next()时返回的是当前结点的数据但是指针已经指到下一个结点去了,可是应该删除的是当前结点
MyLinkedList.this.remove( current.prev );
expectedModCount++;
okToRemove = false;
}
}
}
//测试类
class TestLinkedList{
public static void main( String [ ] args ){
MyLinkedList<Integer> lst = new MyLinkedList<>( );
for( int i = 0; i < 10; i++ )
lst.add( i );
for( int i = 20; i < 30; i++ )
lst.add( 0, i );
lst.remove( 0 );
lst.remove( lst.size( ) - 1 );
System.out.println( lst );
java.util.Iterator<Integer> itr = lst.iterator( );
while( itr.hasNext( ) ){
itr.next( );
itr.remove( );
System.out.println( lst );
}
}
}
本文探讨了链表和数组的区别,指出ArrayList基于数组,适合查询,而LinkedList基于双链表,适合增删。文章详细介绍了如何自定义实现MyArrayList和MyLinkedList,并指出LinkedList增删两端元素速度快,但增删中间元素效率会因查找而降低。

838

被折叠的 条评论
为什么被折叠?



