首先,我们查看List类源代码,查看其中方法,然后用java实现一个我们自己的List<E> 接口类,选择原有List中的大部分方法,利用此接口实现数组以及链表。
零、List接口
首先定义接口 List<E> ,以下是对每个方法的详细解释:
int size(): // 返回列表中元素的数量。
boolean isEmpty(): //判断列表是否为空,如果为空则返回true,否则返回false。
boolean contains(Object o): //判断列表是否包含指定的元素。如果包含则返回true,否则返回false。
Object[] toArray(): //将列表转换为一个数组。
boolean add(Object o): //将指定的元素添加到列表的末尾,并返回true。
boolean remove(Object o): //从列表中移除指定的元素,如果成功移除则返回true,否则返回false。
boolean containsAll(List<E> c): //判断列表是否包含另一个集合中的所有元素,如果是则返回true,否则返回false。
boolean addAll(List<? extends E> c): //批量添加,将另一个集合中的所有元素添加到列表的末尾,如果成功添加则返回true。
boolean addAll(int index, List<? extends E> c): //在指定位置插入另一个集合中的所有元素。
boolean removeAll(List<?> c): //移除列表中与另一个集合相同的所有元素。
void clear(): //移除列表中的所有元素,使其为空。
E get(int index): //返回指定位置的元素。
E set(int index, E element): //将指定位置的元素替换为新的元素,并返回原来的元素。
void add(int index, E element): //在指定位置插入一个元素。
E remove(int index): //移除并返回指定位置的元素。
int indexOf(Object o): //返回指定元素在列表中第一次出现的位置索引,如果不存在则返回-1。
一、数组
查看ArrayList的实现,可以看到ArrayList类继承自AbstractList类,而AbstractList类又实现了List接口。
我们同样利用定义的接口List<E>,创建MyArrayList类来实现其中方法。
- MyArrayList类实现了 List 接口,并使用泛型来表示列表的元素类型。
- 类中的成员变量:
value: 用于存储列表元素的数组,初始长度为零。
size: 当前列表中的元素个数。
length: 表示数组当前的长度,即最大容量。
DEFAULT_LENGTH: 默认的最大容量,设定为30。
1. MyArrayList 类
构造方法:根据传入的 initiallength 初始化数组长度,但不允许小于等于零。
如果 initiallength 小于等于默认容量(30),则使用默认容量初始化数组。
否则,使用传入的 initiallength 初始化数组。
public MyArrayList (int initiallength){
if (initiallength <= 0){
throw new IllegalArgumentException("initiallength 不能小于0");
}
if (initiallength <= 30){
length = DEFAULT_LENGTH;
value = new Object[length];
size=0;
}
else{
length = initiallength;
value = new Object[length];
size=0;
}
}
2.方法实现:
import java.util.Arrays;
public class MyArrayList<E> implements List<E> {
//类成员变量
Object[] value = {}; //长度为零的数组
private int size;//当下坐标,当前存储的元素个数
private int length;//表示数组当前的长度 最大容器
private static final int DEFAULT_LENGTH = 30;//默认最大容量
//构造方法
public MyArrayList(int initiallength){
if(initiallength <= 0){
throw new IllegalArgumentException("初始长度initiallength不能小于0");
}
if (initiallength <= 30 ){
length = DEFAULT_LENGTH;
value = new Object[length];
size = 0;
}
else{
length = 30;
value = new Object[length];
size = 0;
}
}
//元素个数
@Override
public int size() {
return size;
}
//是否为空
@Override
public boolean isEmpty() {
return size==0;
}
//是否包含指定元素
@Override
public boolean contains(Object o) {
for (int i = 0; i < size; i++) {
if(value[i].equals(o)){
return true;
}
}
return false;
}
//将列表转换为数组
@Override
public Object[] toArray() {
return Arrays.copyOf(value,size);
}
//向列表中添加元素
@Override
public boolean add(Object o) {
if(size <= DEFAULT_LENGTH){
value[size++] = o;
}
return true;
}
//从列表中移除指定的元素
@Override
public boolean remove(Object o) {
for (int i = 0; i < size; i++) {
if (value[i].equals(o)){
value = null;
for (int x = i; x < size; x++) {
value[x] = value[i+1];
}
}
}
return true;
}
//判断列表是否包含另一个集合中的所有元素
@Override
public boolean containsAll(List c) {
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
boolean found = false;
for (int j = 0; j < size; j++) {
if (value[j].equals(element) && value[j]!= null){
found = true;
break;
}
}
if (!found){
return false;
}
}
return true;
}
//将另一个集合中的所有元素添加到列表末尾
@Override
public boolean addAll(List<? extends E> c) {
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
value[size++]=element;
}
return true;
}
//在指定位置插入另一个集合中的所有元素
@Override
public boolean addAll(int index, List<? extends E> c) {
if(index < 0 || index > size ){
throw new IllegalArgumentException("Index is out of bounds. Index: \" + index + \", Size: \" + size");
}
int newSize = size + c.size();
if (newSize > length){
int newCapacity = Math.max(length*2,newSize);
}
for (int i = size - 1; i >= index ; i--) {
value[i+c.size()] = value[i];
}
for (int i = 0; i < c.size(); i++) {
value[index+i] = c.get(i);
}
size = newSize;
return true;
}
//移除列表中与另一个集合相同的所有元素.
@Override
public boolean removeAll(List c) {
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
for (int j = 0; j < size; j++) {
if(value[j] ==element){
value[j] = null;
for (int k = j; k < size; k++) {
value[k] = value[k+1];
}
}
}
}
return false;
}
//移除列表中的所有元素
@Override
public void clear() {
for (int i = 0; i < size; i++) {
value[i]=null;
}
size = 0;
}
//获取指定位置的元素
@Override
public E get(int index) {
if (index >= 0 && index < size){
return (E) value[index];
}else{
throw new IllegalArgumentException("index 超过了范围 max:"+(size-1));
}
}
//替换指定位置的元素
@Override
public E set(int index, E element) {
value[index] = element;
return (E) value[index];
}
//在指定位置插入一个元素
@Override
public void add(int index, E element) {
//index后的元素向后移动一个位置
for(int i=size; i>index; i--){
value[i-1]=value[i];
}
value[index]=element;
}
@Override
public E remove(int index) {
//移除指定位置的元素
if(index<0||index>size-1) {
throw new ArrayIndexOutOfBoundsException("index 超过了范围 max:" + (size - 1));
}else {
value[index] = null;
for (int i = index; i < size; i++) {
value[i]= value[i+1];
}
}
return null;
}
//获取第一个匹配元素的索引
@Override
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (value[i].equals(o)){
return i;
}
}
return -1;
}
}
3.数组的特点:
- 连续的内存空间: 数组中的元素在内存中是连续存储的,这使得对数组的随机访问和遍历操作非常高效。
- 相同数据类型: 数组中的元素必须是相同的数据类型,这是因为数组是一个固定大小的结构,每个元素占据相同的内存空间。
- 固定大小: 数组的大小是固定的,一旦创建,其大小通常不能动态改变。
- 随机访问: 由于元素的连续存储,可以通过索引直接访问数组中的任何元素,使得随机访问变得非常高效。
常见的数组操作包括:
- 访问元素: 通过索引可以快速访问数组中的任何元素。
- 插入元素: 在数组中插入元素可能需要将后续元素向后移动,这可能是一个耗时的操作。
- 删除元素: 删除数组中的元素同样可能需要将后续元素向前移动,同样可能是一个耗时的操作。
- 遍历: 遍历数组中的所有元素,执行特定的操作。
- 查找元素: 在数组中查找特定元素,可以使用线性查找或者二分查找等算法。
- 更新元素: 修改数组中特定位置的元素值。
二、链表
查看LinkedList,知晓其实现了 List 接口和 Deque 接口,而它本身是直接继承自 AbstractSequentialList 类。
接下来是一个使用链表实现的自定义泛型列表类 MyLinkedList,实现了之前提到的 List 接口。
1.Node 类:
Node<E> 是一个节点类,包含一个数据域 value 和一个指向下一个节点的引用 next。
构造方法 Node(E o, Node<E> next) 用于初始化节点。
class Node<E> {
E value;
Node<E> next;
public Node(E o, Node<E> next){
this.value = o;
this.next = next;
}
}
2.MyLinkedList 类:
- 实现了
List
接口,使用泛型来表示列表的元素类型。 - 成员变量包括
root
(链表的头节点)、last
(链表的尾节点)、size
(链表中的元素个数)
public class TLinkedList<E> implements List{
Node<E> root;
Node<E> last;
int size;
}
3.方法实现
package day01_ArrayList_LinkList;
public class MyLinkedList<E> implements List{
Node<E> root;
Node<E> last;
int size;
//两种构造方法:
//接受一个元素作为参数,创建包含该元素的链表
public MyLinkedList(E e){
if(e == null){
throw new NullPointerException ("o:" + e);
}
root = new Node<>(e, null);
size++;
}
//无参数构造方法,用于创建空链表。
public MyLinkedList(){ }
//——————————主要方法实现——————————————
//返回链表中的元素个数
@Override
public int size() {
return size;
}
//判断链表是否为空
@Override
public boolean isEmpty() {
return size==0;
}
//判断链表是否包含指定元素
@Override
public boolean contains(Object o) {
Node tempNode = root;
while (tempNode!=null){
if(tempNode.value.equals(o)){
return true;
} else {
tempNode = tempNode.next;
}
}
return false;
}
// 将链表转换为数组
@Override
public Object[] toArray() {
Object[] array = new Object[size];
Node<E> temoNode = root;
int index = 0 ;
while (temoNode != null){
array[index++] = temoNode.value;
temoNode = temoNode.next;
}
return new Object[0];
}
//向链表末尾添加元素
@Override
public boolean add(Object o) {
//判断root是否为null
if (root == null){
root = new Node<>((E)o,null);
size++;
return true;
}
//循环查找 最后一个节点 迭代
Node<E> tempNode = root;
while (tempNode != null ){
tempNode = tempNode.next;
}
tempNode.next = new Node<>((E)o,null);
size++;
return true;
}
//从链表中移除指定元素
@Override
public boolean remove(Object o) {
Node<E> tempNode = root;
while (tempNode != null ){
if(tempNode.value.equals(o)){
tempNode.value = tempNode.next.value;
tempNode.next = tempNode.next.next;
}
tempNode = tempNode.next;
}
size --;
return true;
}
//判断链表是否包含另一个集合中的所有元素
@Override
public boolean containsAll(List c) {
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
boolean found = false;
Node<E> tempNode = root ;
while (tempNode != null){
if (tempNode.value.equals(element)){
found = true;
break;
}else {
tempNode = tempNode.next;
}
}
if(!found){
return false;
}
}
return true;
}
//将另一个集合中的所有元素添加到链表末尾
@Override
public boolean addAll(List c) {
if (c == null) {
throw new IllegalArgumentException("The input list cannot be null");
}
for (int i=0 ; i < c.size() ; i++) {
Object element = c.get(i);
// 判断 root 是否为null
if(root == null){
root = new Node<>( (E)element, null);
size++;
}
// 循环查找 最后一个节点
Node<E> tempNode = root;
while(tempNode.next != null){
// 替换
tempNode = tempNode.next;
}
tempNode.next = new Node<>((E)element, null);
size++;
}
return true;
}
//在指定位置插入另一个集合中的所有元素
@Override
public boolean addAll(int index, List c) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
if (index == 0) {
// 在链表头部插入新节点
root = new Node<>((E) element, root);
} else {
Node<E> tempNode = root;
for (int j = 0; j < index - 1; j++) {
tempNode = tempNode.next;
}
tempNode.next = new Node<>((E) element, tempNode.next);
}
size ++;
index ++;
}
return true;
}
//移除链表中的所有元素
@Override
public void clear() {
Node<E> tempNode = root;
while(tempNode != null){
// 替换
tempNode.value = null;
tempNode = tempNode.next;
}
size = 0;
}
//获取指定位置的元素
@Override
public Object get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
Node<E> tempNode = root;
for (int i = 0; i < index; i++) {
tempNode = tempNode.next;
}
return tempNode.value;
}
//替换指定位置的元素
@Override
public Object set(int index, Object element) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
Node<E> tempNode = root;
for (int j = 0; j < index; j++) {
tempNode = tempNode.next;
}
tempNode.value = (E) element;// 直接更新指定索引处节点的值
return true;
}
//在指定位置插入一个元素
@Override
public void add(int index, Object element) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
if (index == 0) {
// 插入到链表头部
root = new Node<>((E) element, root);
} else {
Node<E> tempNode = root;
//遍历到index前一个Node
for (int i = 0; i < index - 1; i++) {
tempNode = tempNode.next;
}
//tempNode.next指向了原先索引位置的节点
tempNode.next = new Node<>((E) element, tempNode.next);//
}
size++;
}
//移除指定位置的元素
@Override
public Object remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
Node<E> tempNode = root;
for (int j = 0; j < index-1; j++) {
tempNode = tempNode.next;
}
tempNode.next=tempNode.next.next;
size--;
return true;
}
//获取第一个匹配元素的索引
@Override
public int indexOf(Object o) {
int index=0;
Node<E> tempNode = root;
while(tempNode != null){
if(tempNode.value == o){
return index;
}else{
tempNode = tempNode.next;
index=index+1;
}
}
return -1;
}
//移除链表中与另一个集合相同的所有元素
@Override
public boolean removeAll(List c) {
boolean modified = false;
for (int i = 0; i < c.size(); i++) {
Object element = c.get(i);
Node<E> tempNode = root;
Node<E> prevNode = null;
while (tempNode != null) {
if (tempNode.value.equals(element)) {
if (prevNode == null) {
// 移除头节点
root = tempNode.next;
} else {
prevNode.next = tempNode.next;
}
size--;
modified = true;
break; // 一旦找到并移除元素,就退出循环
}
prevNode = tempNode;
tempNode = tempNode.next;
}
}
return modified;
}
}
4.链表的特点
-
非连续存储:链表中的元素在内存中不是连续存储的,而是通过指针(引用)相互连接起来的。
-
动态插入与删除:由于链表中的元素不需要连续存储,因此在链表中插入和删除节点非常高效,平均时间复杂度为O(1)。
-
随机访问性能较差:由于链表中的元素不是连续存储的,因此不能像数组一样通过索引直接访问元素,而是需要通过遍历链表来找到指定位置的元素。
-
不浪费内存空间: 链表可以动态分配内存,不需要预先指定固定大小,因此不会浪费内存
-
链表的种类:单向链表、双向链表和循环链表是常见的链表类型。单向链表中每个节点只有一个指向下一个节点的指针;双向链表中每个节点有一个指向下一个节点的指针和一个指向前一个节点的指针;循环链表是一种特殊的链表,其中尾节点指向头节点,形成一个环状结构。
三、两者区别(可进行拓展)
ArrayList类继承自AbstractList类,而AbstractList类又实现了List接口。因此,ArrayList类通过继承AbstractList类实现了List接口的大部分方法,同时也包含了一些自己的特定实现。ArrayList类提供了对动态数组的支持,可以根据需要动态增长和缩小。
LinkedList 类实际上实现了 List 接口和 Deque 接口,而它本身是直接继承自 AbstractSequentialList 类。这是因为 LinkedList 是一个双向链表,所以它实现了 Deque 接口,同时也可以被当作 List 使用。 AbstractSequentialList 是针对顺序访问序列的一个抽象类,而 LinkedList 则实现了这个抽象类,因此可以使用 AbstractSequentialList 中定义的方法。