目录
什么是顺序表
顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。顺序表是线性表(由n个具有相同特性的数据元素组成的有限序列,物理地址不一定连续) 的一种。
实现一个简单的顺序表
•实现顺序表的属性
要想实现一个顺序表的类,首先需要确定它的属性,其中 elem 是存储数据的数组,usedSide 是数组中有效数字的个数,DEFAULT_SIZE 则是表示数组的初始大小:public class MyArrayList { private int[] elem;//被操作的数组 private int usedSize;//记录数组中有效数字的个数 private static final int DEFAULT_SIZE = 10;//数组的初始大小 }
•实现顺序表的构造方法
一个类除了属性外还得有它的构造方法,我们可以在构造方法中对数组 elem进行初始化:
public MyArrayList() { this.elem = new int[DEFAULT_SIZE]; }
初始化好数组后就可以实现操作数组 elem 的方法了,就从获取顺序表的长度开始吧~
•实现获取顺序表长度的方法size()
因为 usedSize 记录了数组中有效数字的个数,故直接返回usedSize即可获取顺序表长度:
// 获取顺序表长度 public int size() { return this.usedSize; }
•实现在数组最后新增元素的方法add()
接着实现能在数组后添加元素的方法,添加元素时需要注意,➀.如果当前数组满了,则需要扩容后再添加,➂.添加元素后记录有效数字的 usedSide 需要加一:
// 新增元素,默认在数组最后新增 public void add(int data) { //判断当前数组是否满了 if (isFull()) { //满了就需要扩容 this.elem = Arrays.copyOf(this.elem, 2*this.elem.length); } //添加新元素 this.elem[usedSize] = data; //添加后有效数字加一 usedSize++; } //判断数组是否放满了 public boolean isFull() { return size() >= this.elem.length; }
•实现在指定位置新增元素的方法add()
➀在指定位置新增元素同样需要判断数组是否满了,满了需要先扩容,➁.然后还要判断插入的位置是否合法,不合法抛出自定义的异常,➂.再将指定位置后面的元素整体往后挪出一个位置,➃.将新元素插入到指定位置,➄.最后不用忘记有效数字要加一:
// 在 pos 位置新增元素 public void add(int pos, int data) throws PosWrongfulException { //判断当前数组是否满了 if (isFull()) { //满了就需要扩容 this.elem = Arrays.copyOf(this.elem, 2*this.elem.length); } //判断 pos 位置是否合法 if (pos < 0 || pos > this.usedSize) { throw new PosWrongfulException("add():pos 位置不合法!"); } //挪动数据 for (int i = usedSize-1; i >= pos; i--) { this.elem[i+1] = this.elem[i]; } //插入数据 this.elem[pos] = data; //插入后有效数字加一 usedSize++; }
自定义异常类PosWrongfulException:
public class PosWrongfulException extends RuntimeException{ public PosWrongfulException() { } public PosWrongfulException(String message) { super(message); } }
•实现判定是否包含某个元素的方法contains()
判定是否包含某个元素直接遍历一遍数组即可:
// 判定是否包含某个元素 public boolean contains(int toFind){ for (int i = 0; i < this.size(); i++) { if (this.elem[i] == toFind) { return true; } } return false; }
•实现查找某个元素对应的位置的方法indexOf()
同样遍历一遍数组即可:
// 查找某个元素对应的位置 public int indexOf(int toFind) { for (int i = 0; i < this.size(); i++) { if (this.elem[i] == toFind) { return i; } } return -1; }
•实现获取指定位置元素的方法get()
要获取指定位置的元素需要➀.判断当前数组是否为空,➁.指定的位置是否合法,➂.最后再返回指定位置的元素
// 获取 pos 位置的元素 public int get(int pos) { //判断数组是否为空 if (isEmpty()) { throw new EmptyException("get():当前顺序表为空!"); } //判断 pos 位置是否合法 if (pos < 0 || pos >= size()) { throw new PosWrongfulException("get():pos 位置不合法!"); } //返回 pos 位置元素 return this.elem[pos]; } //判断当前数组是否为空 public boolean isEmpty() { return size() == 0; }
自定义异常类EmptyException:
public class EmptyException extends RuntimeException { public EmptyException() { } public EmptyException(String message) { super(message); } }
•实现给指定位置的元素设值的方法set()
需要➀.先判断当前数组是否为空,➁.指定的位置是否合法,➂再更新指定位置的元素的值
// 给 pos 位置的元素设为 value public void set(int pos, int value) { //判断数组是否为空 if (isEmpty()) { throw new EmptyException("set():当前顺序表为空!"); } //判断 pos 位置是否合法 if (pos < 0 || pos >= size()) { throw new PosWrongfulException("set():pos 位置不合法!"); } //更新 pos 位置元素的值 this.elem[pos] = value; }
•实现删除第一次出现的某个数字的方法remove()
要想删除数组中首次出现的某个数字得➀.先保证当前数组不为空,➁.再去寻找该数字的位置,➂.判断是否找的到,找不到抛出异常,找到了将指定位置后面的元素一起往前挪一个位置,➃.最后不要忘记记录有效数字个数的属性值减一
//删除第一次出现的关键字key public void remove(int key) { //判断当前数组是否为空 if (isEmpty()) { throw new EmptyException("remove():当前顺序表为空!"); } //找到第一次出现的数字key int index = this.indexOf(key); //判断是否找到第一次出现的数字key if (index == -1) { throw new NoFoundException6("remove:找不到关键字 key"); } //将后面的元素逐个往前挪 for (int i = index; i < size()-1; i++) { this.elem[i] = this.elem[i+1]; } //删掉一个元素usedSize减一 usedSize--; }
自定义异常类NoFoundException:
public class NoFoundException6 extends RuntimeException { public NoFoundException6() { } public NoFoundException6(String message) { super(message); } }
最后来个简单的:
•实现清空数组的方法clear()
因为是整形数组,所以直接令有效数字为零就好了
// 清空顺序表 public void clear() { this.usedSize = 0; }
注意,若数组存放的是引用类型的话还需要将每个位置的内存给释放掉。
ArrayList
通过上面自己实现的 MyArrayList 类我们对 ArrayList 就有了基本的了解,接下来就一起来揭开Java集合框架中 ArrayList 的面纱~
什么是ArrayList
ArrayList 类是一个可以动态修改的数组,是一个动态类型的顺序表,它是在 java.util 包里面的,ArrayList实现了 list,RandomAccess,Serializable,Cloneable 接口:
• ArrayList 实现了 List 接口,表明 ArrayList 是一个线性表
• ArrayList 实现了 RandomAccess 接口,表明 ArrayList 支持随机访问
• ArrayList 实现了 Serializable 接口,表明 ArrayList 是支持序列化的• ArrayList 实现了 Cloneable 接口,表明 ArrayList 是可以克隆 的ArrayList的构造
ArrayList 有三个构造方法:
•ArrayList():构造一个初始容量为10的空列表
ArrayList<Integer> arrayList = new ArrayList<>();
注:在ArrayList()的构造方法里并没有为数组分配内存,只有当第一次添加数组元素时才会为数组分配大小为10的内存空间
•ArrayList(Collection<? extends E> c):构造一个包含指定集合的元素的构造方法。(只要构造方法里传的是实现了Collection接口的,且其泛型类型是E的子类或其本身的都可以)
ArrayList<Integer> arrayList = new ArrayList<>(); ArrayList<Integer> arrayList1 = new ArrayList<>(arrayList);
•ArrayList(int initialCapacity):构造具有指定初始容量的空列表
ArrayList<Integer> arrayList = new ArrayList<>(10);
ArrayList的常见操作方法
方法 描述 boolean add (E e) 尾插 e void add (int index, E element) 将 e 插入到 index 位置 boolean addAl l (Collection<? extends E> c) 尾插 c 中的元素 E remove (int index) 删除 index 位置元素 boolean remove (Object o) 删除遇到的第一个 o E get (int index) 获取下标 index 位置元素 E set (int index, E element) 将下标 index 位置元素设置为 element void clear () 清空 boolean contains (Object o) 判断 o 是否在线性表中 int i ndexOf (Object o) 返回第一个 o 所在下标 int l astIndexOf (Object o) 返回最后一个 o 的下标 List<E> subList (int fromIndex, int toIndex) 截取部分 listimport java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); ArrayList<Integer> arrayList1 = new ArrayList<>(arrayList); //尾插 e arrayList.add(1); arrayList.add(2); arrayList.add(3); //将 e 插入到 index 位置 arrayList.add(0,2); //尾插c中的元素 arrayList1.addAll(arrayList); //删除 index 位置元素 arrayList.remove(0); //删除遇到的第一个 o arrayList.remove(new Integer(2)); //获取下标 index 位置元素 int val = arrayList.get(0); //将下标 index 位置元素设置为 element arrayList.set(0, 3); //判断 o 是否在线性表中 boolean x = arrayList.contains(3); //返回第一个 o 所在下标 int c = arrayList.indexOf(new Integer(3)); //返回最后一个 o 的下标 int d = arrayList.lastIndexOf(new Integer(3)); //截取部分 list List<Integer> list = arrayList.subList(0, 2); //获取list中有效元素个数 int n = arrayList.size(); //清空 arrayList.clear(); } }
注:subList()截取的部分 list 与原本的共用一块内存空间
ArrayList的遍历
ArrayList 可以使用四种方式遍历:直接输出、for循环+下标、foreach、使用迭代器
•直接输出:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); System.out.println(arrayList); } }
运行结果:
•for循环:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); for (int i = 0; i < arrayList.size(); i++) { System.out.print(arrayList.get(i) + " "); } System.out.println(); } }
运行结果:
•foreach:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); for (Integer a : arrayList) { System.out.print(a + " "); } System.out.println(); } }
运行结果:
•使用迭代器:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); Iterator<Integer> it = arrayList.iterator(); while(it.hasNext()) { System.out.print(it.next() + " "); } System.out.println(); } }
运行结果:
ArrayList的扩容机制
1. 检测是否真正需要扩容,如果是准备扩容
2. 预估需要扩容的大小,初步预估按照1.5倍大小扩容 ,如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
3. 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败4. 能扩容成功则 使用 copyOf 进行扩容