学过java的同学都知道,LinkedList 和 ArrayList 是java中两个非常重要的数据结构,也是面试的时候常考的题型,我们平常可能自己用jdk给我们封装好的api非常方便,但是作为一个程序员,我们要有自己造轮子的能力,这样可以极大的提升我们的编程能力。
1.ArrayList
ArrayList 的底层其实是动态数组,如果了解过C++中的vector的同学应该不会陌生,ArrayList和vector的原理类似,vector的底层维护一个泛型数组,ArrayList底层维护的是一个Object类的数组,我们可以思考一下这样做的好处,因为java中Object类是所有类的直接或者间接超类,所以维护一个Object类的数组就可以转换成任意类的数组,加之java中的泛型只能是类类型的,这样更能保护数据的安全性,这也是为什么java要为基本数据类型设计包装类的原因了。
代码部分:
import java.util.Iterator;
/**
* @author DJS
* Date create 17:33 2022/8/30
* Modified By DJS
**/
public class ArrayList <E> implements Iterable<E> {
private static final int DEFAULT_SIZE = 0; // 数组的默认大小
private static final int DEFAULT_CAPACITY = 50; // 数组的默认容量, 默认为50
private int size; // 数组元素个数
private int capacity; // 数组容量大小
private Object[] arr; // 底层维护的数组
/**
* 无参构造方法:
* 构造出一个空的arraylist, 大小和容量都使用默认值
* */
public ArrayList () {
// 将arr 初始化
size = DEFAULT_SIZE;
capacity = DEFAULT_CAPACITY;
arr = new Object[capacity];
}
/**
* method name: add
* param: E data
* return: void
* 利用尾插法将元素插入
* */
public void add(E data) {
// 如果达到最大容量, 进行扩容操作
if (size == capacity) {
capacity *= 2;
Object[] tmp = new Object[capacity];
// 复制数组元素
System.arraycopy(arr, 0, tmp, 0, arr.length);
arr = tmp;
}
// 将数据插入到尾部
arr[size] = data;
size++;
}
/**
* method name: get
* param: int index
* return: E
* 获取index位置的元素
* */
@SuppressWarnings("unchecked")
public E get(int index) {
if (index < 0 || index > size)
throw new ArrayIndexOutOfBoundsException("Index out of range");
return (E)arr[index];
}
/**
* method name: indexOf
* param: E data
* return: int
* 获取值为data的下标
* */
public int indexOf(E data) {
for (int i = 0; i < size; i++)
if (arr[i].equals(data))
return i;
return -1;
}
public boolean findIf(E data) {
for (int i = 0; i < size; i++)
if (arr[i].equals(data))
return true;
return false;
}
public void remove(int idx) {
if (idx < 0 || idx > size) throw new ArrayIndexOutOfBoundsException("Out of array");
if (size - 1 - idx >= 0)
System.arraycopy(arr, idx + 1, arr, idx, size - 1 - idx);
}
@Override
public Iterator<E> iterator() {
return new Iter<>();
}
// implement interface Iterator and Iterable to suppose Enhanced for
private static class Iter<E> implements Iterator<E> {
private int current_idx;
private final ArrayList<E> array;
@Override
public boolean hasNext() {
return ++current_idx < array.size;
}
@Override
@SuppressWarnings("unchecked")
public E next() {
return (E) array.arr[++current_idx];
}
public Iter() {
current_idx = -1;
array = new ArrayList<>();
}
}
}
以上是一个ArrayList的简单实现。
接下来我们来讲解LinkedList, 实际上LinkedList 就是一个双向链表,这是其底层原理,因为java原生的api并没有给我们提供栈和队列这两种数据结构的实现,所以我们通常也会使用LinkedList来模拟栈和队列。
以下是LinkedList手动代码实现
import java.util.Iterator;
/**
* @author DJS
* Date create 17:34 2022/8/30
* Modified By DJS
*
* 双向链表的实现
**/
public final class LinkedList<E> implements Iterator<E>, Iterable<E> {
/**
* head: 头指针
* tail: 尾指针
* size: 链表的元素个数
* */
private int size;
private final Node<E> head;
private final Node<E> tail;
private Node<E> cur_ptr;
@Override
public boolean hasNext() {
return cur_ptr.next != tail;
}
@Override
public E next() {
E data = cur_ptr.next.data;
cur_ptr = cur_ptr.next;
return data;
}
@Override
public Iterator<E> iterator() {
cur_ptr = head;
return this;
}
private static class Node<E> {
/**
* prev: 节点前驱
* next: 节点后继
* data: 数据域
* */
public Node<E> prev;
public Node<E> next;
public E data;
public Node(Node<E> prev, Node<E> next, E data) {
this.prev = prev;
this.next = next;
this.data = data;
}
}
public LinkedList() {
head = new Node<E>(null, null, null);
tail = new Node<E>(head, null, null);
cur_ptr = head;
head.next = tail;
this.size = 0;
}
public LinkedList(int size, E data) {
this();
for (int i = 0; i < size; i++) insert(data);
}
// 在指定位置插入
public void insert(int index, E data) throws Exception {
if (index < 0 || index > size) throw new Exception("Out of index");
if (index == size) {
insert(data);
return;
}
Node<E> cur = head;
for (int i = 0; i < index; i++) cur = cur.next;
// 插入操作
cur.next = new Node<E>(cur, cur.next, data);
cur.next.prev = cur;
cur.next.next.prev = cur.next;
this.size++;
}
// 尾插法
public void insert(E data) {
Node<E> cur = tail.prev;
cur.next = new Node<E>(cur, tail, data);
tail.prev = cur.next;
this.size++;
}
public void insert_head(E data) {
try {
insert(0, data);
} catch (Exception e) {
e.printStackTrace();
}
}
public E front() {
if (head.next != null) return head.next.data;
else return null;
}
public E back() {
if (tail.prev != null) return tail.prev.data;
else return null;
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return this.size == 0;
}
public void remove(int index) {
if (this.isEmpty() || index > size) throw new NullPointerException("None Node");
Node<E> cur = head;
for (int i = 0; i < index; i++) cur = cur.next;
Node<E> tmp = cur.next;
cur.next = tmp.next;
tmp.next.prev = cur;
tmp.next = null;
tmp.prev = null;
this.size--;
}
public void pop() {
if (this.isEmpty()) throw new NullPointerException("None Node");
Node<E> tmp = head.next;
head.next = tmp.next;
tmp.next.prev = head;
tmp.prev = null;
tmp.next = null;
this.size--;
}
public void reverse() {
if (isEmpty()) return;
reverse(head.next);
}
private Node<E> reverse(Node<E> curr) {
if (curr.next == tail) {
head.next = curr;
curr.prev = head;
return curr;
}
Node<E> pre = reverse(curr.next);
pre.next = curr;
curr.prev = pre;
curr.next = tail;
tail.prev = curr;
return curr;
}
public E getMid() {
if (isEmpty()) return null;
Node<E> low = this.head;
Node<E> fast = this.head;
while (fast != tail) {
low = low.next;
fast = fast.next.next;
}
return low.data;
}
}
我再LinkedList中还实现了两个算法,一个是反转链表,另外一个是通过快慢指针取到链表的中间值。具体操作可以参考我提供的实现。