利用Java手写LinkedList
和ArrayList不同的是,LinkedList是采用链表实现的,链表的特点就是每个节点存储的是value和下个节点的地址,所以不存在类似ArrayList的扩容问题,添加节点只需要一个新的节点对象然后链表末尾指向它就可以了。参考Java官方的LinkedList实现:java.util.LinkedList
。不过Java官方使用双向链表实现。
链表和节点
链表有n多个链表节点组成,每个节点存储的都是元素+下个节点的内存地址。如何得到节点中存储的元素实际上是通过从链表的头一个节点开始迭代,然后得到当前节点。
私有属性
动态数组大小size
,以及用于存放该动态数组的头节点first
注意:为什么要有头节点?
是因为链表元素的获取是利用迭代来不断得到next元素的,也就是说你迭代到了中间某一元素后,你就没有办法回溯了,你这个链表就暂时停留在这里了(因为该元素前的所以链表的内存地址已丢失),所以就需要保存一个头节点的内存地址,以确保每次都能够从头开始依次访问链表中的元素
public class LinkedList<E> {
/**
* 动态数组大小
*/
private int size;
/**
* 头节点
*/
private Node<E> first;
}
节点类
通过静态内部类的方式来声明链表中的节点Node类
public class LinkedList<E> {
/**
* 动态数组大小
*/
private int size;
/**
* 头节点
*/
private Node<E> first;
/**
* 静态内部类
*/
private static class Node<E>{
/**
* 该节点存储的元素
*/
private E element;
/**
* 该节点所指向的下一个节点
*/
private Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
Node类主要包含节点存储的具体元素、下个节点的内存地址,以及节点的构造方法(指定元素,以及指定下一个节点)。
通过下标获得具体node节点
链表与数组不同的是,它是利用迭代来定位到具体某个节点的,再很多add、remove的场景中我们可能会经常需要定位到某个具体的节点(因为需要获取该节点前后的节点)
/**
* 获取具体某一下标的节点
* @param index 下标
* @return 该下标的node节点
*/
private Node<E> node(int index){
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
可以看到上述方法就是通过for循环的方式,node.next不断迭代获得node节点,最后获取到位于index位置的node元素。
构造方法
LinkedList不同于ArrayList,因为它不需要初始化数组容量,所以它没有构造函数。
基本方法
和ArrayList类似,LinkedList具有以下基本方法
public class LinkedList<E> {
/**
* 动态数组大小
* @return 动态数组大小
*/
public int size(){
}
/**
* 动态数组是否为空
* @return 动态数组是否为空
*/
public Boolean isEmpty(){
}
/**
* 添加元素
* @param element 元素
*/
public void add(E element){
}
/**
*
* 0 1 2 3 4 5 6 7 8 9
* 1 2 3 4 5 6 7 8
* 向指定位置添加元素
* @param index 位置
* @param element 元素
*/
public void add(int index,E element){
}
/**
*
* 0 1 2 3 4 5 6 7 8 9
* a b c 1 d e f g h
* 移除指定位置的元素
* @param index 位置
*/
public void remove(int index){
}
/**
* 删除指定元素
* @param element 元素
*/
public void remove(E element){
}
/**
* 清空动态数组中的元素
*/
public void clear(){
}
/**
* 修改指定位置的元素
* @param index 位置
* @param element 元素
*/
public void set(int index,E element){
}
/**
* 获得指定位置的元素
* @param index 位置
* @return 元素
*/
public E get(int index){
}
/**
* 判断数组是否包含该元素
* @param element 元素
* @return true包含,false不包含
*/
public Boolean contains(E element){
}
/**
* 该元素第一次出现的下标
* @param element 元素
* @return 下标
*/
public int indexOf(E element){
}
@Override
public String toString() {
}
}
size()
返回动态数组的大小
public int size() {
return size;
}
isEmpty()
返回该动态数组是否为空,即判断size是否为0
public boolean isEmpty() {
return size == 0;
}
toString()
打印该链表,依旧是从头节点开始迭代然后打印
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("LinkedList{");
string.append("size=" + size + ", elements=[");
Node<E> node = first;
for (int i = 0; i < size; i++) {
string.append(node.element);
node = node.next;
if (i!=size-1){
string.append(", ");
}
}
string.append("]");
string.append("}");
return string.toString();
}
indexOf()和contains()
indexOf(E element)就是返回该元素所在的位置下标,如果不存在则返回-1。同样地,利用从first开始迭代node,如果节点的元素等于传入的元素则直接返回该下标。
@Override
public int indexOf(E element) {
Node<E> node = first;
for (int i = 0; i < size; i++) {
if ( (node.element).equals(element) ) return i;
node = node.next;
}
return -1;
}
contains(E element)就是判断链表是否包含该元素,如果包含则返回true。
@Override
public boolean contains(E element) {
return indexOf(element) >= 0;
}
get()
通过节点下标获得该节点
@Override
public E