一、自定义容器
Java中具有用来存取数据的容器,我们可以通过数组来进行容器的编写,四种基本的线性表结构的容器:Stack、Queue、ArrayList、LinkedList。其中Stack、Queue、ArrayList可以通过数组来进行定义。
1.Stack
Stack中文名:栈,是一种先进后出的数据结构。
package 四种常见的线性表结构;
import java.util.Arrays;
public class Stack {
private Object[] data;
private int size;
private int capacity;
public Stack() {
this.capacity = 10;
data = new Object[capacity];
}
public Stack(int capacity) {
this.capacity = capacity;
data = new Object[this.capacity];
}
// 进栈一个元素
public void push(Object e) {
if (this.capacity == this.size) {
int length = this.capacity + (this.capacity >>> 1);
this.resize(length);
} else {
data[this.size++] = e;
}
}
// 出栈一个元素
public Object pop() {
if (this.size == 0) {
throw new RuntimeException("栈已空,无法再弹出!");
} else {
return data[--this.size];
}
}
// 查看栈顶元素
public Object peek() {
return data[this.size - 1];
}
// 扩容或缩容容器的大小
private void resize(int len) {
this.data = Arrays.copyOf(this.data, len);
this.capacity = len;
}
// 获取栈中的元素个数
public int size() {
return this.size;
}
// 判断栈是否为空
public boolean isEmpty(Object o) {
return this.size == 0;
}
// 清空栈
public void clear() {
this.size = 0;
}
// 返回栈的字符串形式
public String toString() {
return Arrays.toString(Arrays.copyOf(this.data, this.size));
}
// 对比两个栈是否相等
public boolean equals(Object o) {
return o == this;
}
//测试
public static void main(String[] args) {
Stack s = new Stack();
s.push(12);
s.push("cxcc");
System.out.println(s);
System.out.println(s.peek());
System.out.println(s.pop());
System.out.println(s.size());
}
}
2.Queue
Queue:队列,是一种先进先出的数据结构。
package 四种常见的线性表结构;
import java.util.Arrays;
public class Queue {
private Object[] data;
private int size;
private int capacity;
public Queue() {
this.capacity = 10;
data = new Object[capacity];
}
public Queue(int capacity) {
this.capacity = capacity;
data = new Object[this.capacity];
}
// 进队一个元素
public void offer(Object e) {
if (this.size == this.capacity) {
int length = this.capacity + (this.capacity >>> 1);
this.resize(length);
} else {
this.data[size++] = e;
}
}
// 出队一个元素
public Object poll() {
if (this.size == 0) {
throw new RuntimeException("队元素已空!");
} else {
this.size--;
Object temp = this.data[0];
System.arraycopy(data, 1, this.data, 0, data.length - 1);
return temp;
}
}
// 查看对首元素
public Object element() {
return this.data[0];
}
// 获取队中的元素个数
public Object peek() {
return this.size;
}
// 清空队列
public void clear() {
this.size = 0;
}
// 返回队列的字符串形式
public String toString() {
return Arrays.toString(Arrays.copyOf(this.data, this.size));
}
// 对比两个队列是否相等
public boolean equals(Object o) {
Queue s = (Queue) o;
if (this.size != s.size()) {
return false;
}
for (int i = 0; i < s.size(); i++) {
if (data[i] != s.data[i]) {
return false;
}
}
return true;
}
// 扩容或缩容容器的大小
private void resize(int len) {
this.data = Arrays.copyOf(this.data, len);
this.capacity = len;
}
public int size() {
return this.size;
}
//测试
public static void main(String[] args) {
Queue stack = new Queue();
stack.offer("155");
stack.offer("sdfsdf");
stack.offer(1123);
stack.offer(0456);
stack.offer(7829);
System.out.println(stack);
stack.poll();
System.out.println(stack);
stack.poll();
System.out.println(stack);
stack.poll();
System.out.println(stack);
stack.poll();
System.out.println(stack);
stack.poll();
System.out.println(stack);
}
}
3.ArrayList
ArrayList 是 java 集合框架中比较常用的数据结构了。继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。我们也可以通过数组自定义一个属于我们自己的ArrayList
package 四种常见的线性表结构;
import java.util.Arrays;
public class ArrayList {
private Object[] data;
private int size;
private int capacity;
public ArrayList() {
this.capacity = 10;
data = new Object[this.capacity];
}
public ArrayList(int capacity) {
this.capacity = capacity;
data = new Object[this.capacity];
}
public void add(Object o) {
flash();
this.data[size++] = o;
}
private void flash() {
if (this.size == this.capacity) {
int length = this.capacity + (this.capacity >>> 1);
this.resize(length);
}
}
public void add(int index, Object o) {
flash();
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = o;
this.size++;
}
private void resize(int len) {
this.data = Arrays.copyOf(this.data, len);
this.capacity = len;
}
public void delete(Object o) {
for (int i = 0; i < size; i++) {
if (data[i] == o) {
for (int j = i; j < size; j++) {
data[j] = data[j + 1];
}
data[size] = null;
}
}
this.size--;
}
public void delete(int index) {
if (index > size || index < 0) {
throw new RuntimeException("不存在");
} else {
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = null;
this.size--;
}
}
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(data, size));
}
public void clear() {
this.size = 0;
}
public Object get(int index) {
if (index < size) {
return "不存在";
}
return this.data[index];
}
}
测试
package 四种常见的线性表结构;
import org.junit.jupiter.api.Test;
class TestArrayList {
@Test
void test() {
ArrayList a=new ArrayList();
a.add("1");
a.add("2");
a.add(3);
System.out.println(a);
a.delete(2);
a.delete("2");
a.delete(3);
System.out.println(a);
}
}
4.LinkedList
链表是一种物理存储结构上不连续的存储结构,是由存储元素的结点连接而成。每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。
链表分为单向链表和双向链表:
单向链表:只能通过前一个节点找到后一个节点或者只能通过后一个节点找到前一个节点,关联关系是单向的;
双向链表:既能通过前一个节点找到后一个节点,同时也能通过后一个节点找到前一个节点,关联关系是双向的。
package 四种常见的线性表结构;
public class LinkedList {
Node first;// 第一个链表
Node last;// 最后一个链表
int size;// 链表数量
public LinkedList() {
}
// 判断元素添加是否成功
boolean add(Object o) {
Node node = new Node(last, o, null);
if (first == null) {
first = node;
last = node;
} else {
last.next = node;
last = node;
}
size++;
return true;
}
// 指定位置添加元素
void add(int index, Object o) {
if (index > size || index < 0) {
throw new IllegalArgumentException();
}
if (index == size - 1) {
add(o);
} else {
Node indexNode = getNode(index);
Node preNode = indexNode.prex;
Node node = new Node(preNode, o, indexNode);
if (preNode == null) {
first = node;
} else {
preNode.next = node;
indexNode.prex = node;
}
}
size++;
}
// 获取某处位置的元素
Node getNode(int index) {
if (index > size || index < 0) {
throw new IllegalArgumentException();
}
Node temp = first;
for (int i = 0; i <= index; i++) {
if (i == index) {
return temp;
}
temp = temp.next;
}
return null;
}
//删除指定下标的元素
void remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException();
}
Node node=getNode(index);
Object res=node.value;
Node preNode=node.prex;
Node nextNode=node.next;
if(preNode==null) {
nextNode.prex=null;
first=nextNode;
}else if (nextNode == null) {
preNode.next = null;
last = preNode;
}else {
preNode.next=nextNode;
nextNode.prex=preNode;
}
size--;
}
//删除指定的元素
boolean remove(Object o) {
Node temp = first;
for (int i = 0; i < size; i++) {
if (temp.value.equals(o)) {
remove(i);
}
temp = temp.next;
}
return false;
}
//清除所有元素
void clear() {
Node temp = first;
while (temp != null) {
Node t = temp.next;
temp.prex = null;
temp.value = null;
temp.next = null;
temp = t;
}
first = null;
last = null;
size = 0;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
Node temp = first;
if(temp != null) {
while (temp != null) {
sb.append(temp.value).append(", ");
temp = temp.next;
}
return sb.substring(0, sb.length() - 2)+"]";
}
return "";
}
// 链表类
class Node {
Node prex;// 上一个节点
Node next;// 下一个节点
Object value;// 当前节点的值
public Node() {
}
public Node(Node prex, Object value, Node next) {
this.prex = prex;
this.next = next;
this.value = value;
}
}
}
测试
package 四种常见的线性表结构;
import org.junit.jupiter.api.Test;
class TestLinkedList {
@Test
void test() {
LinkedList linkedList = new LinkedList();
linkedList.add("aa");
linkedList.add("bb");
linkedList.add("cc");
linkedList.add("dd");
linkedList.remove(2);
linkedList.clear();
System.out.println(linkedList);
}
}
ArrayList部分官方源码解析
1.ArrayList中的各个变量
//长度为0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认容量为0的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//集合存元素的数组
transient Object[] elementData;
//集合的长度
private int size;
//集合默认容量
private static final int DEFAULT_CAPACITY = 10;
2.方法
add(E e)
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
//每增加1个元素,数组所需容量+1,并检查增加数组容量后是否要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//添加元素
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//如果是默认的DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//将两者之中的最大值最为新的容量
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//检查是否需要扩容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//记录操作次数
modCount++;
/*
elementData.length:原集合中实际的元素数量
minCapacity:集合的最小容量
*/
if (minCapacity - elementData.length > 0)
//扩容的核心方法
grow(minCapacity);
}
private void grow(int minCapacity) {
//获取为添加元素之前的集合元素数量,设置为oldCapacity
int oldCapacity = elementData.length;
//右移位运算符:oldCapacity>>1=oldCapacity/2
//新容量为原有容量的1.5倍
//扩容的核心代码
int newCapacity = oldCapacity + (oldCapacity >> 1);
//第一次add时newCapacity为0,故newCapacity - minCapacity会小于0
//如果新容量比所需要的容量还小
if (newCapacity - minCapacity < 0)
//新容量就等于所需要的容量
newCapacity = minCapacity;
//如果新容量比规定的最大容量还大,最新容量等于最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//将原数组拷贝到一个更大容量的数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
add(int index,E element)
在指定索引位置添加元素
public void add(int index, E element) {
//判断所传入的索引是否符合规则(既不能<0,也不能大于原数组容量)
rangeCheckForAdd(index);
//数组所需容量+1,并判断增加元素之后是否需要扩容
ensureCapacityInternal(size + 1);
//数组元素拷贝,index之后的元素向后移一位,把index的位置空了出来
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在index位置上填充要插入的元素
elementData[index] = element;
//元素个数+1
size++;
}
//判断index是否符合规则
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
set(int index,E element)
根据索引修改元素
public E set(int index, E element) {
//判断索引是否符合规则,索引不能超过数组长度
rangeCheck(index);
//获得下标处的元素
E oldValue = elementData(index);
//修改索引处的元素值
elementData[index] = element;
//将旧的元素值返回
return oldValue;
}
//校验索引
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//获得下标处的元素
E elementData(int index) {
return (E) elementData[index];
}
get(int index)
获取索引处的元素值
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//校验索引范围
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//返回索引处元素
E elementData(int index) {
return (E) elementData[index];
}
remove(Object o)
移除指定元素值的元素
public boolean remove(Object o) {
//如果要删除的元素是否为空
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//遍历集合
for (int index = 0; index < size; index++)
//将集合中的每一个元素进行比对,比对成功则删除
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*集合真正删除元素的方法
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
//对集合的实际修改次数+1
modCount++;
//计算要移动的元素的个数
int numMoved = size - index - 1;
//如果移动的元素的个数>0
if (numMoved > 0)
//移动元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将要删除的元素置为null,就是为了尽快被垃圾回收机制回收
elementData[--size] = null;
}
clear()
清空元素
public void clear() {
//修改次数
modCount++;
//将数组中的每个元素都设为null
for (int i = 0; i < size; i++)
elementData[i] = null;
//数组元素个数清空
size = 0;
}