文章目录
🚀 一、线性表
线性表是n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
🚀 二、顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况采用数组存储,在数组上完成增删查改。
🌟 2.1 接口的实现
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SeqList{
public int[] elem;
public int usedSize; // 记录当前数组中数据个数
public SeqList(){
elem = new int[10];
usedSize = 0;
}
/**
* 打印顺序表,
*/
public void display(){
for(int i = 0; i<this.usedSize;i++){
System.out.print(elem[i] + " ");
}
System.out.println();
}
/**
* 顺序表尾插
* @param data
*/
public void add(int data){
if(isFull()){
elem = Arrays.copyOf(elem,elem.length*2);
}
this.elem[usedSize++] = data;
}
/**
* 判断是否满
* @return
*/
private boolean isFull(){
return usedSize == this.elem.length;
}
/**
* 在 pos 位置新增元素
* @param pos
* @param data
*/
public void add(int pos,int data){
try{
// 首先判断 pos 位置是否合法
checkAddPos(pos);
if(isFull()){
// 扩容
elem = Arrays.copyOf(elem,elem.length*2);
}
for(int i = usedSize-1;i >= pos;i--){
this.elem[i+1] = this.elem[i];
}
this.elem[pos] = data;
usedSize++;
}catch (IndexOutOfBoundsException e){
e.printStackTrace();
}
}
/**
* 判断 pos 位置是否合法
* @param pos
*/
private void checkAddPos(int pos){
if(pos < 0 || pos >= elem.length){
throw new IndexOutOfBoundsException("pos 位置不合法");
}
}
/**
* 判断是否包含元素
* @param key
* @return
*/
public boolean contains(int key){
for(int i = 0; i< elem.length;i++){
if(elem[i] == key){
return true;
}
}
return false;
}
/**
* 查找某元素的位置
*
* @param key
* @return
*/
public int indexOf(int key){
for(int i = 0; i< elem.length;i++){
if(elem[i] == key){
return i;
}
}
return -1;
}
/**
* 获得 pos 位置元素
*
* @param pos
* @return
*/
public int get(int pos){
checkAddPos(pos);
return elem[pos];
}
/**
* 改变 pos 位置元素
* @param pos
* @param value
*/
public void set(int pos,int value){
checkAddPos(pos);
this.elem[pos] = value;
}
/**
* 删除第一次出现的关键字 key
* @param key
*/
public void remove(int key){
int index = indexOf(key);
if(index == -1){
System.out.println("数字错误");
return;
}
for(int i = index;i<usedSize;i++){
this.elem[i] = this.elem[i+1];
}
usedSize--;
}
/**
* 获取顺序表长度
* @return
*/
public int size(){
return usedSize;
}
/**
* 清空顺序表
*/
public void clear(){
usedSize = 0;
}
}
🚀 三、ArraysList 简介
❗ 注意:
- ArrayList 实现了 Cloneable 接口,表名它是可克隆的。
- ArrayList 实现了 RandomAccess 接口,表名它支持随机访问。
- 与 Vector 不同,ArrayList 不是线程安全的,在单线程下可以使用,多线程下选择 Vector 或者 CopyOnWriteArrayList。
- ArrayList 底层是一段连续空间,可以动态扩容,是一个动态的顺序表。
🚀 四、ArraysList 使用
4.1 🌟 ArrayList 构造
4.2 🌟 ArrayList 常用操作
4.3 🌟 ArrayList 遍历
ArrayList 可以使用三种方式遍历:
- for 循环 + 下标。
- foreach
- 迭代器
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
list.add(323);
list.add(35);
// 1. for 循环
for(int i = 0;i < list.size();i++){
System.out.print(list.get(i) + " ");
}
System.out.println();
// 2. foreach 循环
for(Integer x : list){
System.out.print(x +" ");
}
System.out.println();
// 3. 使用迭代器
Iterator it = list.iterator();
while(it.hasNext()){
System.out.print(it.next() + " ");
}
System.out.println();
}
4.3 🌟 ArrayList 的扩容机制
以下是 ArrayList 源码中的扩容方式:
Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 获取旧空间大小
int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0){
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
总结
- 检测是否真正需要扩容,如果是,调用 grow 进行扩容。
- 预估需要扩容的大小。
- 初步预估 1.5 倍大小扩容。
- 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
- 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
- 使用 copyOf 进行扩容。