【java数据结构】顺序表
- 一、了解List接口
- 二、顺序表
- 2.1 线性表
- 2.2 顺序表
- 2.2.1 顺序表接口的实现
- 给数组增加新元素
- 判断数组数据是否为满
- 在 pos 位置新增元素
- 判定是否包含某个元素
- 查找某个元素对应的位置
- 获取 pos 位置的元素
- 给 pos 位置的元素设为 value
- 删除第一次出现的关键字key
- 获取顺序表长度
- 清空顺序表
- 打印顺序表
- 三、ArrayList类
- 3.1 ArrayLIst方法
- 3.1.1 构造方法
- 3.1.2 常用方法
- 3.1.3 ArrayLIst的初始容量以及扩容
- 3.1.4 ArrayLIst的迭代
- 3.2 ArrayLIst存在问题
此篇博客希望对你有所帮助(帮助里了解List接口,顺序表以及java集合框架中,实现List接口,顺序表的一种具体实现ArrayLIst类),不懂的或有错误的也可在评论区留言,错误必改评论必回!!!持续关注,下一篇博客是ArrayLIst的具体实现,两个例题帮你更加清晰准确理解ArrayLIst类!!!
一、了解List接口
在Java中,List接口是一个非常重要的集合框架接口,它继承自Collection接口(Collection接口继承Iterable接口)。List接口定义了一个有序集合,允许我们存储元素集合。并且可以根据元素的索引来访问集合中的元素。这意味着List中的每个元素都有其固定的位置,可以通过索引来访问和修改。
List接口中包含的方法(因为它拓展了Collection接口和Iterable接口中的功能):
List接口的实现类有很多,其中最常用的有ArrayList和LinkedList。它们各自有不同的特点和性能表现:
ArrayList:基于动态数组的实现,随机访问性能很好,但在列表的开头和中间插入、删除元素时性能较差,因为需要移动其他元素。
LinkedList:基于链表的实现,在插入、删除元素时性能较好,尤其是当这些操作发生在列表的开头或中间时,但在随机访问元素时性能较差。
二、顺序表
2.1 线性表
线性表是n个具有相同特征的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表,链表,栈,队列…
线性表在逻辑上时线性结构,也就是说连续的一条直线。但是在物理结构上并不一定是连续的。线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.2 顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成增删改查。
2.2.1 顺序表接口的实现
顺序表的接口包含以下方法:
public interface IList {
//给数组增加新元素
public void add(int data);
//判断数组数据是否为满
boolean isFull();
// 在 pos 位置新增元素
public void add(int pos, int data);
// 判定是否包含某个元素
public boolean contains(int toFind);
// 查找某个元素对应的位置
public int indexOf(int toFind);
// 获取 pos 位置的元素
public int get(int pos);
// 给 pos 位置的元素设为 value
public void set(int pos, int value);
//删除第一次出现的关键字key
public void remove(int toRemove);
// 获取顺序表长度
public int size() ;
// 清空顺序表
public void clear();
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() ;
}
给数组增加新元素
public void add(int data) {
//首先需要先判断数组是否已经存满
if(isFull()){
grow();
}
array[useSize]=data;
useSize++;
}
判断数组数据是否为满
@Override
public boolean isFull() {
//相等返回true;反之,返回false
return useSize== array.length;
}
在 pos 位置新增元素
这里需要先考虑插入数组下标是否合理,所以需要自己写一个自定义异常!
//自定义的数组下标插入异常
class PosExpection extends RuntimeException{
public PosExpection(String message){
super(message);
}
}
//自定义的异常:判断数组是否为空
class EmptyException extends RuntimeException{
public EmptyException(String message){
super(message);
}
}
//这个是私有方法,只是为了在这个类中检查数组下标是否合理,
// 所以用private修饰
private void checkPos(int pos) throws PosExpection{
if(pos < 0 || pos >= useSize){
throw new PosExpection("数组下标异常");
}
}
@Override
public void add(int pos, int data) {
try {
checkPos(pos);
//如果插入数组下表没问题,则判断是否需要扩容
if(isFull()){
grow();
}
}catch (PosExpection e){
System.out.println("插入数组下标不合理...");
e.printStackTrace();
}
//这里挪动数组,将Pos下标之后的数组往后挪
for (int i = useSize-1; i >=pos ; i--) {
array[i+1]= array[i];
}
array[pos]=data;
}
判定是否包含某个元素
@Override
public boolean contains(int toFind) {
for (int i = 0; i < useSize; i++) {
if(array[i]==toFind){
return true;
}
}
return false;
}
查找某个元素对应的位置
@Override
public int indexOf(int toFind) {
for (int i = 0; i < useSize; i++) {
if(array[i]==toFind){
return i;
}
}
return 0;
}
获取 pos 位置的元素
@Override
public int get(int pos) {
try {
checkPos(pos);
return array[pos];
}catch (PosExpection e){
System.out.println("数组下标不合理...");
e.printStackTrace();
}
return 0;
}
给 pos 位置的元素设为 value
public void set(int pos, int value) {
try {
checkPos(pos);
array[pos]=value;
}catch (PosExpection e){
System.out.println("数组下标不合理...");
e.printStackTrace();
}
}
删除第一次出现的关键字key
@Override
public void remove(int toRemove) {
int pos=indexOf(toRemove);
if(pos==-1){
return;
}
for (int i = pos; i <useSize-1 ; i++) {
array[i]=array[i+1];
}
useSize--;
}
获取顺序表长度
@Override
public int size() {
return useSize;
}
清空顺序表
@Override
public void clear() {
useSize=0;
}
打印顺序表
@Override
public void display() {
for (int i = 0; i < useSize; i++) {
System.out.print(array[i]+" ");
}
//这里不能for-each遍历,
// 用for-each遍历不管数组里面有没有数据,都会遍历出和数组大小一样的元素,对应下标没有元素会用0来代替。
// for (int x:
// array) {
// System.out.println(x+" ");
// }
}
三、ArrayList类
在集合框架中,ArrayList是一个类,实现了List接口。
ArrayList实现了List接口,而List接口在数据结构的角度上就是线性表的一种抽象。因此,ArrayList可以看作是顺序表在Java集合框架中的一种具体实现。
实现接口:
- ArrayList是通过泛型的方式实现的,使用前必须先实例化。
- ArrayList实现了RandomAccess接口,表明ArrayList类支持随机访问。
- ArrayLIst实现了Cloneable接口,表明ArrayLIst支持Clone的。
- ArrayLIst实现了Serializable接口,表明ArrayLIst支持可序列化。
- ArrayLIst不是线程安全的,在单线程下是可以使用的。
- ArrayLIst是一段连续的空间,并且可以动态扩容,是一个动态类型的线性表。
3.1 ArrayLIst方法
3.1.1 构造方法
3.1.2 常用方法
ArrayLIst的常用方法和上面实现顺序表接口的实现差不多。
3.1.3 ArrayLIst的初始容量以及扩容
1.ArrayList的初始容量是指创建ArrayList对象时所分配的内存空间大小。在Java中,如果没有为ArrayList指定初始容量,它将自动分配一个默认的初始容量0。
2.当向ArrayList中添加元素时,如果元素的数量超过了当前容量,ArrayList会自动扩容。默认的扩容机制是将容量增加原来容量的1.5倍。需要注意的是,扩容操作会重新分配内存空间,并将原有元素复制到新空间中,这个过程需要消耗一定的时间。
3. 如果你在创建ArrayList对象时指定了初始容量,那么ArrayList的初始容量就是这个指定的容量值。例如,如果你写“ArrayList list = new ArrayList(10);”,那么list的初始容量就是10。
3.1.4 ArrayLIst的迭代
1.for循环迭代数组列表中的元素:
package demo1;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> arrList = new ArrayList<>();
arrList.add("hahah");
arrList.add("hdbsh");
arrList.add("回家");
//循环遍历数组
for (int i = 0; i < arrList.size(); i++) {
System.out.println(arrList.get(i));
}
}
}
运行结果:
2.for-each迭代数组列表元素:
public static void main(String[] args) {
ArrayList<Integer> arrList = new ArrayList<>();
arrList.add(1);
arrList.add(2);
arrList.add(8);
//循环遍历数组
for (int i:
arrList) {
System.out.println(i);
}
}
运行结果:
3.迭代器(Iterator)遍历数组列表元素:
调用ArrayList的iterator方法来获取一个迭代器对象,然后使用while循环和hasNext方法来遍历ArrayList中的元素。在每次循环中,我们调用next方法来获取下一个元素,并将其打印到控制台上。
public static void main(String[] args) {
ArrayList<String> arrList = new ArrayList<String>();
arrList.add("26565");
arrList.add("26655");
arrList.add("22");
// 使用迭代器遍历ArrayList
Iterator<String> it = arrList.iterator();
while (it.hasNext()) {
String element = it.next();
System.out.println(element);
}
}
运行结果:
3.2 ArrayLIst存在问题
1.ArrayLIst 底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或往后挪动,故时间复杂度为O(n);
2.增容需要申请空间,拷贝数据,释放旧空间,会有不小的消耗;
3.增容一般呈2倍增长,势必会有一定的空间浪费。例如当容量为100,瞒不了以后增容到200,我们再继续插入5个数据,后面没有数据插入了,那么就浪费了95个空间。