线性表是n个具有相同特性的数据元素的有限序列。常见的线性表:顺序表,链表,栈,队列等。线性表在逻辑上是线性的,但在物理结构上并不一定连续,通常以数组或链式结构存储。
1、顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据的线性结构,一般采用数组存储。
自己实现一个ArrayList进行增删改查。
import java.util.Arrays;
public class MyArrayList {
public int[] elem;
public int usedSize; //当前存放的数据个数
public static final int DEFAULT_CAPACITY = 2;
public MyArrayList() {
this.elem = new int[DEFAULT_CAPACITY];
}
// 打印顺序表
public void display(){
for (int i = 0; i < this.usedSize; i++) {
System.out.println(this.elem[i] + " ");
}
System.out.println();
}
// 新增元素,默认在数组最后新增
public void add(int data) {
if (isFull()){
//扩容
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize] = data;
usedSize++;
}
private boolean isFull(){
return (usedSize == elem.length);
}
// 在 pos 位置新增元素
public void add(int pos, int data) {
try {
//判断pos位置是否合法
checkAddPos(pos);
if (isFull()){
elem = Arrays.copyOf(elem,2*elem.length);
}
for (int i = usedSize; i > pos ; i--) {
elem[i] = elem[i-1];
}
elem[pos] = data;
usedSize++;
}catch (PosIndexNotLegaException e){
e.printStackTrace();
}
}
private void checkAddPos(int pos){
if (pos < 0 || pos > usedSize){
throw new PosIndexNotLegaException("pos位置不合法");
}
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < usedSize; i++) {
if (elem[i] == toFind){
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < usedSize; i++) {
if (elem[i] == toFind){
return i;
}
}
return -1;
}
// 获取 pos 位置的元素
public int get(int pos) {
checkGetPos(pos);
return elem[pos];
}
private void checkGetPos(int pos){
if (pos < 0 || pos >= usedSize){
throw new PosIndexNotLegaException("pos位置不合法");
}
}
// 给 pos 位置的元素设为 value
public void set(int pos, int value) {
//覆盖之前的值
checkGetPos(pos);
elem[pos] = value;
}
//删除第一次出现的值
public void remove(int toRemove) {
int index = indexOf(toRemove);
for (int i = index; i < usedSize-1; i++){
elem[i] = elem[i+1];
}
usedSize--;
}
// 获取顺序表长度
public int size() {
return usedSize;
}
// 清空顺序表
public void clear() {
usedSize = 0;
}
}
测试结果:
2、ArrayList
ArrayList
是一个普通的类,实现了
List
接口。
-> ArrayList
实现了
RandomAccess
接口,表明
ArrayList
支持随机访问;
-> ArrayList实现了
Cloneable
接口,表明
ArrayList
是可以
clone
的;
-> ArrayList实现了
Serializable
接口,表明
ArrayList
是支持序列化的;
->
ArrayList不是线程安全的
,在单线程下可以使用,在
多线程中可以选择Vector或者CopyOnWriteArrayList;
-> ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表。
2-1 ArrayList的构造
(1)无参构造:
ArrayList<Integer> arrayList = new ArrayList<>();
(2)指定顺序表初始容量的构造方法:
ArrayList<Integer> arrayList = new ArrayList<>(5);
(3)
ArrayList<Integer> arrayList1 = new ArrayList<>(arrayList); arrayList1构造好之后,与 arrayList中的元素一致
2-2 ArrayList的常见方法
boolean
add
(E e) :尾插 e;
void
add
(int index, E element) :将 e
插入到
index
位置;
boolean
addAll
(Collection<? extends E> c) :尾插 c
中的元素;
E
remove
(int index) :删除 index
位置元素;
E
get
(int index) :获取下标 index
位置元素;
E
set
(int index, E element) :将下标 index
位置元素设置为
element;
void
clear
() :清空;
boolean
contains
(Object o) :判断 o
是否在线性表中;
int
indexOf
(Object o) :返回第一个 o
所在下标;
List<E>
subList
(int fromIndex, int toIndex):截取部分list【前闭后开】;
2-3 ArrayList的遍历
(1)for循环+下标遍历
(2)foreach遍历
(3)使用迭代器
2-4 顺序表的问题
1.
顺序表中间
/
头部的插入删除,时间复杂度为
O(N)。【需要遍历】
2.
增容需要申请新空间,然后拷贝数据,释放旧空间。产生不小的消耗。
3.
增容一般是呈
2
倍的增长,必定会产生空间浪费。例如当前容量为
50
,满了以后增容到
100
,我们再继续插入了3
个数据,后面没有数据插入,那么就浪费了
47
个数据空间。