目录:
一、List的简单的介绍:
在我们了解 List 之前呢,我们要先看一个图片:
我们可以看到这个图片的最左边呢就是我们List,这个List继承了 Collection 接口,Collection接口又继承了 Iterable,接口,所以我们的 List 接口就有了这两个接口的所有方法。
在我们站在数据结构的角度来看呢,List 接口就是一个线性表,就是n个具有相同类型的元素的有限序列 ,在这个序列上可以进行增删查改的操作。
注意我们的List是一个接口,不能实例化,要是使用的话,只能去实例化List的实现类。
二、线性表:
常见的线性表为:顺序表、链表、栈、队列....
对于线性表我们从逻辑上来说呢,其是线性结构,相当于是连续的一条直线,线性表在存储时候呢,通常以数组和链式结构来存储的。
三、顺序表:
我们呢先来介绍线性表中的顺序表。我们来看看:
顺序表呢,是使用一段物理地址连续的存储单元依次存储数据,一般情况下呢,我们使用数组进行存储,在数组上进行对元素的增删查改操作。
这次能我们就是用数组来进行对数据的增删查改操作,在我们使用数组的时候呢,数组自带的方法不能满足我们的使用,这时候呢我们就自己定义一个类,让这个类来进行对方法的实现,以便于我们使用。
那么接下来我们自己定义一个类,来进行对数组的增删查改等操作。
我们来看看它的代码的每一步的实现:
1、基本代码:
我们的类里里面用的是数组,所以我们要在类中定义一个数组:
由上面的图可知并且我们要使用构造方法来初始化数组:
对于类的基本成员就结束了,接下来我们就差操作方法了,接下来看我们来看看,对于操作方法我们可以定义一个接口,便于我们管理:
那么我们如果我们的数组长度不够了,我们想要改变其数组容量的话,要怎么办?那我们是不是可以把其长度定义成成员变量,使其进行改变:
这里我们还有一个需要注意的点就是,我们这个类是实现了IList这个接口的,所以我们可以向上转型来调用方法:
Ok,我们对于这个顺序表的基本的都已经写完了,接下来我们就开始一一实现这里的方法了
2、操作代码:
display()方法:
打印顺序表。
这个是非常简单的,就是对数组的遍历,我们再一一打印出来,我们来看看代码的实现:
这个代码呢,是有一些问题的,我们用的是数组的长度,但是呢对于数组是不是可能不是每一个下标都放数据了,所以呢,我们只需要有效的数据就可以了,而且当我们有效的数据很小而我们的数组长度很大,是不是就浪费时间了呢?
所以这里哦我们呢要用有效的数组长度:
我们调用一下代码,看看会不会报错:
我们来对比下,当我们使用数组长度的时候会输出什么呢:
add(int data)方法:
新增元素,默认在数组的最后添加。在写代码之前我们先来看个图:
是不是看到这个图片,觉得对于add代码是不是非常简单呢?
是不是有人这样写的呢?
那你想的太简单了︿( ̄︶ ̄)︿,如果我们添加数据,要是满了怎么办呢?所以不是很简单的,那么这个正确代码就是:
我们可以看到,代码执行之后是没有问题的。
add(int pos,int data)方法:
和add(int data)构成重载方法,这个是在指定的位置放入数据,我们来看看它的原理: 我们来看看代码是怎样实现的:
我们来看看其代码运行的结果会不会报错:
我们可以看到没有异常发生,我们再看看当我们pos不合法会输出什么:
contains(int toFind)方法:
查找和 toFind 这个数据相等的元素,有就返回true,没有就返回false。这个方法就是非常简单的了,就是遍历一次数组就可以了,我们来看看图:
接下来我们来看看,这个方法的代码的实现:
我们来执行一下看看:
我们可以看到没有任何的问题。
indexOf(int toFind)方法:
这个方法和上面的方法是类似的,这个是返回其toFind的下标这个位置,所以我们的代码都差不多的。
get(int pos)方法:
我们返回这个 pos 位置的下标的值,这个代码我们乍一看是不是觉得很简单呢?那你看对了,这个代码很简单,但是我们需要谨慎,我们要在这个代码执行执行之前呢,要查找两个方面是否有异常:
1、pos位置不能 < 0 或者不能 >= usedSize这个有效数组长度。
2、我们要看看我们的顺序表也就是数组是否为空我们来看代码:
我们执行一下看看有没有问题:
set(int pos,int value)方法:
这个方法和上面的方法是类似的,我们也是需要判断两次异常,才能执行代码。
我们直接看代码和执行结果:
remove(int toRemove)方法:
这个方法呢,是删除这个第一次出现的这个toRemove这个值
我们先来看看代码的实现:
我们可以看到我们的代码是没有问题的。
size()方法:
获得数组的有效长度,这个代码是非常简单的。我们来看:
clear()方法:
这个方法也是很简单的,我们对于这些代码是不是都是对于有效长度来说的,所以当我们把有效数组长度设置为0时呢,我们是不是就把顺序表清空了呢? 所以这个是非常简单的:
对于这样的clear方法只能处理一些基本的数据类型,我们正常的写法呢,应该是:
但是为什么报错呢,因为我们现在是基本数据类型,不涉及到内存的泄露,直接把有效长度赋值为0,就可以了。
我们来看一下整体的代码是什么样的:
3、整体代码:
package list;
public interface IList {
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
void display();
// 新增元素,默认在数组最后新增
void add(int data);
// 在 pos 位置新增元素
void add(int pos, int data);
// 判定是否包含某个元素
boolean contains(int toFind);
// 查找某个元素对应的位置
int indexOf(int toFind);
// 获取 pos 位置的元素
int get(int pos);
// 给 pos 位置的元素设为 value
void set(int pos, int value);
//删除第一次出现的关键字key
void remove(int toRemove);
// 获取顺序表长度
int size();
// 清空顺序表
void clear();
}
package list;
import java.util.Arrays;
public class MyArrayList implements IList{
public int[] array;
public int usedSize;//数组的有效的个数
//默认的容量,长度不变的
public static final int DEFAULT_SIZE = 10;
public MyArrayList() {
this.array = new int[DEFAULT_SIZE];//长度为十,默认初始化值为0
}
@Override
public void display() {
// 打印顺序表
for (int i = 0; i < this.usedSize; i++) {
System.out.print(array[i] + " ");
}
System.out.println("");
}
@Override
public void add(int data) {
// 新增元素,默认在数组最后新增
if (isFull()) {
//满了,不能放数据了,我们要进行扩容
Expansion();
}
//进行添加数据的操作
this.array[this.usedSize] = data;
this.usedSize++;
}
//对于判满这个操作,我们在别的方法中也会使用
//所以我们可以单独创建一个方法
public boolean isFull() {
//判满
return this.usedSize == DEFAULT_SIZE;
}
private void Expansion() {
//扩容操作,我们不想让别的类调用这个方法,我们只想在自己的类中调用
this.array = Arrays.copyOf(this.array,
2*this.array.length);
//我们一般按照2倍或者1.5倍进行扩容
}
private void checkPos(int pos) throws PosIsNotlegally{
if (pos < 0 || pos > usedSize) {
throw new PosIsNotlegally("Pos位置不合法");
}
}
@Override
public void add(int pos, int data) {
// 在 pos 位置新增元素
try{
checkPos(pos);//判断pos这个位置合不合法
if (isFull()) {
Expansion();
}
int end = this.usedSize;
while(pos != end) {
this.array[end] = this.array[end - 1];
end--;
}
this.array[pos] = data;
this.usedSize++;
}catch(PosIsNotlegally e) {
System.out.println("输入的Pos位置不合法");
e.printStackTrace();
}
}
@Override
public boolean contains(int toFind) {
// 判定是否包含某个元素
for (int i = 0; i < this.usedSize; i++) {
if (this.array[i] == toFind) {
return true;
}
}
return false;
}
@Override
public int indexOf(int toFind) {
// 查找某个元素对应的位置
for (int i = 0; i < this.usedSize; i++) {
if (this.array[i] == toFind) {
return i;
}
}
return -1;
}
private void checkPos2(int pos) throws PosIsNotlegally{
if (pos < 0 || pos >= usedSize) {
throw new PosIsNotlegally("Pos的位置不合法");
}
}
@Override
public int get(int pos) {
// 获取 pos 位置的元素
try {
//判断是否出现异常
checkPos2(pos);
checkEmpty();
//没有异常的话,直接返回pos下标的值
return this.array[pos];
}catch (PosIsNotlegally e) {
System.out.println("pos的位置不合法");
e.printStackTrace();
}catch (EmptyException e) {
System.out.println("顺序表为空");
e.printStackTrace();
}
return -1;
}
private boolean isEmpty() {
return usedSize == 0;
}
private void checkEmpty() throws EmptyException{
if (isEmpty()) {
throw new EmptyException("顺序表为空");
}
}
@Override
public void set(int pos, int value) {
// 给 pos 位置的元素设为 value
try {
//这个也要判断异常
checkPos2(pos);
checkEmpty();
//没有异常给pos位置赋值
this.array[pos] = value;
}catch (PosIsNotlegally e) {
System.out.println("pos位置不合法");
e.printStackTrace();
}catch (EmptyException e) {
System.out.println("顺序表为空");
e.printStackTrace();
}
}
@Override
public void remove(int toRemove) {
//删除第一次出现的关键字toRemove
try{
checkEmpty();
int pos = indexOf(toRemove);
if (pos == -1) {
return;
}
for (int i = pos; i < usedSize - 1; i++) {
array[i] = array[i + 1];
}
usedSize--;
}catch (EmptyException e) {
System.out.println("顺序表为空");
e.printStackTrace();
}
}
@Override
public int size() {
// 获取顺序表长度
return this.usedSize;
}
@Override
public void clear() {
// 清空顺序表
// for (int i = 0; i < usedSize; i++) {
// array[i] = null;
// }
usedSize = 0;
}
}
package list;
public class PosIsNotlegally extends RuntimeException{
public PosIsNotlegally() {
}
public PosIsNotlegally(String message) {
super(message);
}
}
package list;
public class EmptyException extends RuntimeException{
public EmptyException() {
}
public EmptyException(String message) {
super(message);
}
}
四、ArrayList简介:
上面的呢,是我们用Java自己实现的 ArrayList 类,但是呢,对于Java来说呢,不像C语言,Java里面是有 ArrayList 的,不像C语言只能自己实现。
但是呢,我们自己定义的MyArrayList这个类和编译器自带的 ArrayList 这个类是大同小异的。
但是呢,在我们使用编译器里面自带的ArrayList这个类的时候我们有一些需要注意的地方:
注意:
1、ArrayList是以泛型方式实现的,在使用时必须要进行初始化。
2、ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
3、ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
4、ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
对于Java自带的顺序表ArrayList来说,遍历顺序表呢就是直接打印就可以了。
五、ArrayList的使用:
1、ArrayList的构造方法:
对于我们想要是用ArrayList呢,我们是不是要先创建对象啊,所以我们来看看对于Java里自带的ArrayList的构造方法是什么样的:
对于Java自带的构造方法呢,我们有三种构造方法:
1、无参构造:
2、有参构造:
创建一个自定的参数的顺序表。
3、传表来构造:
对于都可以传入什么值,我们来看看这个构造方法的源代码:
这里是要实现了Collection这个接口, 我们这个 ?是通配符,我们后面介绍,这里的 ?是E或者是E的子类
2、ArrayList的常见操作:
add方法:
1、add(E e)
相当于我们自己写的add方法,也是在尾部插入。
2、add(int index,E element)
在index位置插入element这个值。
3、addAll(Collection < ? extends E > c)
将c中的元素全部进行尾插操作。
remove方法:
1、remove(int index):
删除index下标的的元素。
2、remove(Object o)方法:
删除数据o
get(int index)方法:
获得index位置的数据
这个方法和我们自己实现的是差不多的,这里我们就不实现了
set(int index,E element)方法:
将index下标的数据设置为element
clear()方法:
清空
contains(Object o)方法:
判断o是否在顺序表中
indexOf(Object o)方法:
返回第一个o所出现的下标
lastIndexOf(Object o)方法:
返回最后一个o的下标。
这些方法呢,和我们自己实现的都差不多,这里我就不在重复实现了。
subList(int fromIndex,int toIndex)方法:
截取部分list。
当然除了这些方法呢,我们ArrayList还有方法,这些我们在使用的时候就可以去查一查
3、ArrayList的遍历:
1、我们可以直接使用打印顺序表,来进行打印:
2、for循环输出:
3、for-each循环遍历:
4、使用迭代器来进行遍历:
总结:
OK,我们关于Java中自带的 ArrayList 的方法就已经简单的介绍完事了,这次的博客呢,也到这里结束了,下次我们要写几个关于ArrayList的题,让我们期待下次的见面,再见,拜拜~~~