1、我们先来聊聊ArrayList
大家也知道面试以及开发中我们常用ArrayList集合存储元素,那么你知道为什么要使用ArrayList来存储元素吗?
一张图带你走进面试常问知识点???

ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。
数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。

简单来说:原生数组长度一旦确定,就不能改变。(数组方法少)集合弥补了这些缺点。 2、ArrayList和LinkList的区别?
图解查询操作比较:

LinkList底层为双向链表,每一个节点块为一个内存区域,当需要查找时,顺着每个节点一直查询下去,会导致好多小内存的开销,所以查询效率低。
图解增删操作比较:

LinkList底层为双向链表,每一个节点块为一个内存区域,当需要增删时,只需要断开该节点,释放此小块内存空间,指针连接下一个元素即可,不需要对所有元素进行移动,所以增删效率高。
画重点:
当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进 行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
3、手写实现ArrayList
使用动态数组实现ArrayList
主要设计接口:


public class MyArrayList<T> {
private static final int DEFAULT_CAPACITY=10;//定义数组初始化容量
private int size;//数组下一次要添加元素的下标
private T[] elements;//数组元素
//无参构造方法
public MyArrayList() {
//为集合创建初始化容量
elements = (T[])new Object[DEFAULT_CAPACITY];
}
第二步:添加元素方法
1、逐个添加元素

//逐个进行添加元素
public void add(T element) {
elements[size++] = element;
}
2、指定位置添加元素

//指定位置处添加元素
public void add(int index,T element) {
//判断index是否越界
if(index<0 || index>size) {
throw new IndexOutOfBoundsException();
}
//从后往前遍历集合,找到对应位置,插入即可
for(int i = size;i>index;i--) {
//将元素统一往后面移动,上一个元素为i的值
elements[i] = elements[i-1];
}
//将要添加的元素添加到指定的索引处
elements[index] = element;
//数组下标+1
size++;
}
第三步:扩容操作
如何扩容:创建一个新的动态数组,ArrayList扩容为原来的1.5倍,引用将从原数组指向新数组,并且原数组没了引用,将被GC回收掉。

//扩容操作
public void addCapacity(){
//判断index是否越界
if(size<elements.length) {
return;
}else {
//开始扩容(位运算:16>>3 是等价于16/2的3次方 16<<3 是等价于16*2的3次方)
int newCapacity = elements.length+(elements.length>>1);// *1.5倍
//为新的动态数组创建新容量
T[] newElements = (T[])new Object[newCapacity];
for(int i = 0;i<size;i++) {
//将原集合的值赋值给新集合中
newElements[i] = elements[i];
}
//将新集合赋给原集合(注意,这相当于把引用重新指向新集合中,原集合被GC当作垃圾回收)
elements = newElements;
}
}
第四步:删除操作

//删除元素操作
public T remove(int index) {
//判断index是否越界
if(index<0 || index>=size) {
throw new IndexOutOfBoundsException();
}else {
T old = elements[index];
for(int i = index;i<size;i++) {
//数组元素逐个向前移动,进行赋值
elements[i] = elements[i+1];
}
size--;
//将最后一个元素置为null,因为最后一个元素向前移动了,原位置处还有引用。
//置为null可以防止内存泄漏,回收引用,便于GC垃圾回收
elements[size] = null;
//返回原集合
return old;
}
}
第五步:set方法:
//设置元素操作
public int set(int index,T element) {
//判断index是否越界
if(index<0 || index>=size) {
//抛出下标越界异常
throw new IndexOutOfBoundsException();
}else {
elements[index] = element;
}
return 0;
}
第六步:get方法:
//获取元素操作
public T get(int index) {
//判断index是否越界
if(index<0 || index>=size) {
//抛出下标越界异常
throw new IndexOutOfBoundsException();
}else {
return elements[index];
}
}
第七步:获取集合元素操作
//获取集合大小操作
public int size() {
return size;
}
第八步:清空集合操作(注意:防止内存泄漏)
//清空集合操作
public void clear() {
//置为null可以防止内存泄漏,回收引用,便于GC垃圾回收
for(int i = 0;i<size;i++) {
elements[i] = null;
}
//设置size位0即可
size=0;
}
第九步:判空操作
//判断集合是否为空
public boolean isEmpty() {
return size == 0? true:false;
}
第十步:toString操作
//重写toString方法
@Override
public String toString() {
StringBuffer sb = new StringBuffer().append("{");
for(int i = 0;i<size;i++) {
sb.append(elements[i].toString()).append(",");
}
//修改最后一个字符
sb.setCharAt(sb.length()-1,'}');
return sb.toString();
}
自定义实现ArrayList完整代码:
package com.baoji.shoudong_arrayList;
import java.util.Arrays;
/**
* @author LinChi
*
* @param <T>
*/
public class MyArrayList<T> {
private static final int DEFAULT_CAPACITY=10;//定义数组初始化容量
private int size;//数组下一次要添加元素的下标
private T[] elements;//数组元素
//无参构造方法
public MyArrayList() {
//为集合创建初始化容量
elements = (T[])new Object[DEFAULT_CAPACITY];
}
//逐个进行添加元素
public void add(T element) {
add(size,element);
}
//指定位置添加元素
public void add(int index,T element) {
//判断index是否越界
if(index<0 || index>size) {
throw new IndexOutOfBoundsException();
}
//扩容操作
addCapacity();
//从后往前遍历集合,找到对应位置,插入即可
for(int i = size;i>index;i--) {
//将元素统一往后面移动,上一个元素为i的值
elements[i] = elements[i-1];
}
elements[index] = element;
size++;
}
//扩容操作
public void addCapacity(){
if(size<elements.length) {
return;
}else {
//开始扩容(位运算:16>>3 是等价于16/2的3次方 16<<3 是等价于16*2的3次方)
int newCapacity = elements.length+(elements.length>>1);// *1.5倍
T[] newElements = (T[])new Object[newCapacity];
for(int i = 0;i<size;i++) {
//将原集合的值赋值给新集合中
newElements[i] = elements[i];
}
//将新集合赋给原集合(注意,这相当于把引用重新指向新集合中,原集合被GC当作垃圾回收)
elements = newElements;
}
}
//删除元素操作
public T remove(int index) {
//判断index是否越界
if(index<0 || index>=size) {
throw new IndexOutOfBoundsException();
}else {
T old = elements[index];
for(int i = index;i<size;i++) {
elements[i] = elements[i+1];
}
size--;
//将最后一个元素置为null,因为最后一个元素向前移动了,原位置处还有引用。
//置为null可以防止内存泄漏,回收引用,便于GC垃圾回收
elements[size] = null;
//返回原集合
return old;
}
}
//设置元素操作
public int set(int index,T element) {
//判断index是否越界
if(index<0 || index>=size) {
throw new IndexOutOfBoundsException();
}else {
elements[index] = element;
}
return 0;
}
//获取元素操作
public T get(int index) {
//判断index是否越界
if(index<0 || index>=size) {
throw new IndexOutOfBoundsException();
}else {
return elements[index];
}
}
//获取集合大小操作
public int size() {
return size;
}
//清空集合操作
public void clear() {
//置为null可以防止内存泄漏,回收引用,便于GC垃圾回收
for(int i = 0;i<size;i++) {
elements[i] = null;
}
//设置size位0即可
size=0;
}
//判断集合是否为空
public boolean isEmpty() {
return size == 0? true:false;
}
//重写toString方法
@Override
public String toString() {
StringBuffer sb = new StringBuffer().append("{");
for(int i = 0;i<size;i++) {
sb.append(elements[i].toString()).append(",");
}
//修改最后一个字符
sb.setCharAt(sb.length()-1,'}');
return sb.toString();
}
}
敲黑板:
每次当对象没有了作用时,必须要赋值为null,更好的解决了内存泄漏问题,及时的让GC进行垃圾回收,增加可用的内存空间。
通过对源码的解析,自定义实现ArrayList的重要方法,学习东西一定要有深度!!!
2、我们下来来聊聊LinkedListList
上面我们详细提到了ArrayList和LinkedList的区别,接下来我们用双向链表实现LinkedList
1、创建节点类
package com.yueqian.list;
/**
* 创建节点类
* @author LinChi
*
* @param <E>
*/
public class ListNode<E> {
//集合元素
private E element;
//集合索引位置
private int index;
//下一个节点对象
private ListNode<E> next;
public ListNode(int index, E element){
this.index = index;
this.element = element;
}
public E getElement() {
return element;
}
public void setElement(E element) {
this.element = element;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public ListNode<E> getNext() {
return next;
}
public void setNext(ListNode<E> next) {
this.next = next;
}
}
2、LinkedList具体实现增删改查方法
package com.yueqian.list;
public class MyList<E> {
//定义集合大小
private int size;
//定义第一个节点
private ListNode<E> first;
/**
* 集合大小
* @return
*/
public int size(){
return size;
}
/**
* 添加元素
*/
public boolean add(E e){
//根据传入元素,构建新节点
ListNode<E> newNode = new ListNode<E>(size, e);
//得到最后一个节点
ListNode<E> lastNode = getLastNode();
//判断列表现在是否空列表
if(lastNode == null){
first = newNode;
//累加size
size++;
return true;
}
//列表不为空
lastNode.setNext(newNode);
//累加size
size++;
return true;
}
/**
* 插入元素
*/
public void add(int index, E element){
//判断非法输入
if(index < 0 || index > size){
throw new IndexOutOfBoundsException();
}
//可能是空列表
//判断是否添加都到末尾
if(index == size){
add(element);
return;
}
//至少有一个元素
//是否向 index = 0 的位置添加
if(index == 0){
//新建节点
ListNode<E> newNode = new ListNode<E>(0, element);
//记录first当前指向的第一个节点
ListNode<E> reNode = first;
//整理角标
changeIndex(0, true);
//让frist指向新节点
first = newNode;
//让新节点记录旧的第一个节点
newNode.setNext(reNode);
size++;
return;
}
//至少有两个元素
//新建节点
ListNode<E> newNode = new ListNode<E>(index, element);
//记录当前列表中index位置的节点,准备是新节点的下一个节点
ListNode<E> nextNode = this.getIndexNode(index);
//记录上一个节点
ListNode<E> priNode = this.getIndexNode(index - 1);
//整理角标
changeIndex(index, true);
//上一个节点存储新节点
priNode.setNext(newNode);
//让新节点存储下一个节点
newNode.setNext(nextNode);
size++;
}
/**
* 按照指定位置添加集合元素
* @param c
* @return
*/
public boolean addAll(int index,MyList<? extends E> c) {
//非法判断
if(c == null) {
throw new NullPointerException();
}
if(c.isEmpty()) {
return false;
}
for(int i = c.size-1;i>=0;i--) {
this.add(index,c.get(i));
}
return true;
}
/**
* 指定位置处获得元素
* @param index
* @return
*/
public E get(int index){
//获取指定的节点
ListNode<E> indexNode = getIndexNode(index);
return indexNode.getElement();
}
/**
* 格式化字符串集合
*/
public String toString(){
//判断是否空列表
if( size == 0){
return "{}";
}
StringBuffer sbuf = new StringBuffer();
sbuf.append("{");
for (int i = 0; i < this.size; i++) {
sbuf.append(get(i)).append(", ");
}
//去掉最后一个,
sbuf.delete(sbuf.length()-2, sbuf.length());
sbuf.append("}");
return sbuf.toString();
}
/**
* 按照索引位置处展示集合元素
* @param fromIndex
* @param toIndex
* @return
*/
public MyList<E> subList(int fromIndex, int toIndex){
//过滤非法输入
if(fromIndex < 0 || toIndex > size || fromIndex >= toIndex){
throw new IndexOutOfBoundsException();
}
MyList<E> subList = new MyList<E>();
for (int i = fromIndex; i < toIndex; i++) {
subList.add(this.get(i));
}
return subList;
}
/**
* 判断集合是否为null
* @return
*/
public boolean isEmpty(){
if(this.size == 0){
return true;
}
return false;
}
/**
* 添加所有
* @param c
* @return
*/
public boolean addAll(MyList<? extends E> c){
//过滤非法输入
if(c == null){
throw new NullPointerException();
}
//c 是否 empty
if( c.isEmpty() ){
return false;
}
//循环 c 挨个将c的元素加入this
for (int i = 0; i < c.size(); i++) {
this.add(c.get(i));
}
return true;
}
/**
* 按照指定位置删除集合中的元素
* @param index
* @return
*/
public E remove(int index){
//过滤非法输入
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
//获取返回结果
E result = this.get(index);
//判断是否移除最后一个节点
if(index == size - 1){
//是否只有一个元素
if( size <= 1){
this.first = null;
}else{
//找到倒数第二个节点,并且置空倒数第二个节点对最后一个节点的引用
this.getIndexNode(size - 2).setNext(null);
}
}else if(index == 0){
//判断是否移除第一个节点 index = 0 ,当前至少有2个元素
//获取第二个节点的引用
ListNode<E> node = this.getIndexNode(1);
//变更角标, 减 1
this.changeIndex(1, false);
//让first直接获取第二个元素节点
first = node;
}else{
//删除中间节点, 至少有三个元素
//获取index之前的节点
ListNode<E> priNode = this.getIndexNode(index - 1);
//获取index之后的节点
ListNode<E> nextNode = this.getIndexNode(index + 1);
//变更角标, 减 1
this.changeIndex(index + 1, false);
//让之前的节点绕过index位置,直接获取nextNode节点的引用
priNode.setNext(nextNode);
}
size--;
return result;
}
/**
* 删除集合元素
* @param c
* @return
*/
public boolean removeAll(MyList<?> c){
//判断集合是否为null
if(c == null) {
throw new NullPointerException();
}
//判断集合中是否为null列表
if(c.isEmpty()) {
return false;
}
//定义返回结果
boolean result = false;
//循环删除c集合中的元素
for(int i = 0;i<c.size;i++) {
boolean flag = this.remove(c.get(i));
result = result || flag;
while(flag) {
flag = this.remove(c.get(i));
}
}
return result;
}
/***
* 按照集合对象找出索引位置
* @param o
* @return
*/
public int indexOf(Object o){
int result = -1;
for (int i = 0; i < this.size; i++) {
//判断是否同一个对象
if(this.get(i) == o){
result = i;
break;
}
//过滤null元素
if(this.get(i) == null){
continue;
}
//判断是否内容一样
if(this.get(i).equals(o)){
result = i;
break;
}
}
return result;
}
/**
* 寻找最后一个相同元素返回的索引位置
* @param o
* @return
*/
public int lastIndexOf(Object o) {
// int result = -1;
//
// for (int i = this.size-1; i >=0 ; i--) {
// //判断是否同一个对象
// if(this.get(i) == o){
// result = i;
// break;
// }
// //过滤null元素
// if(this.get(i) == null){
// continue;
// }
// //判断是否内容一样
// if(this.get(i).equals(o)){
// result = i;
// break;
// }
// }
// return result;
int result = -1;
for(int i= 0;i<this.size;i++) {
int index = this.size-i-1;
//判断是否为同一个对象
if(this.get(index) == o) {
result = index;
break;
}
//判断是否为null
if(this.get(index) == null) {
continue;
}
//判断是否内容一样
if(this.get(index).equals(o)){
result = index;
break;
}
}
return result;
}
/**
* 在集合中删除对象
* @param o
* @return
*/
public boolean remove(Object o) {
//查找当前对象
int index = this.indexOf(o);
if(index == -1) {
return false;
}
this.remove(index);
return true;
}
/**
* 清除所有元素
*/
public void clear() {
this.size = 0;
this.first = null;
}
/**
* 为集合指定位置设置值
* @param index
* @param element
* @return
*/
public E set(int index, E element) {
ListNode<E> node = this.getIndexNode(index);
E result = node.getElement();
//替换
node.setElement(element);
return result;
}
/**
* 判断集合中是否包含
* @param o
* @return
*/
public boolean contains(Object o) {
// for(int i = 0;i<size;i++) {
// if(o == this.get(i)) {
// return true;
// }
// if(o.equals(this.get(i))) {
// return true;
// }
// }
// return false;
return this.indexOf(o) == -1?false:true;
}
/**
* 判断集合中是否包含其他集合
* @param c
* @return
*/
public boolean containsAll(MyList<?> c) {
//判断对象是否为null
if(c == null) {
throw new NullPointerException();
}
for(int i = 0;i<c.size;i++) {
if(!(this.contains(c.get(i)))) {
return false;
}
}
return true;
}
public Object[] toArray() {
//判断当前对象是否为null
if(this.isEmpty()) {
return new Object[0];
}
//创建数组对象
Object[] result = new Object[this.size];
for(int i = 0;i<size;i++) {
result[i] = this.get(i);
}
return result;
}
public boolean retainAll(MyList<?> c) {
if(c == null) {
throw new NullPointerException();
}
if(c.isEmpty()) {
this.clear();
return true;
}
boolean result = false;
int thisSize = this.size;
for(int i = thisSize;i>=0;i--) {
if(!c.contains(c.get(i))) {
this.remove(i);
result = true;
}
}
return result;
}
private ListNode<E> getLastNode(){
if(this.first == null){
return null;
}
ListNode<E> node = first;
while(node.getNext() != null){
node = node.getNext();
}
return node;
// return getIndexNode(size - 1);
}
private ListNode<E> getIndexNode(int index){
//判断是否参数非法
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
//循环获取指定位置的节点
ListNode<E> node = first;
while(node.getIndex() != index){
node = node.getNext();
}
return node;
}
/**
*
* @param index
* @param flag == true 加角标, 否则 减角标
*/
private void changeIndex(int index, boolean flag){
//判断是否参数非法
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
ListNode<E> node = getIndexNode(index);
while(node != null){
if(flag){
node.setIndex(node.getIndex() + 1);
}else{
node.setIndex(node.getIndex() - 1);
}
node = node.getNext();
}
}
}
3、测试LinkedList集合
package com.yueqian.list;
import java.util.ArrayList;
import java.util.List;
public class TestList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
// List<String> list2 = new ArrayList<String>();
// MyList<String> list = new MyList<String>();
// MyList<String> list2 = new MyList<String>();
list.add("a");
list.add("x");
list.add("b");
list.add("c");
list.add("b");
list.add("d");
list.add("e");
// list2.add("a");
// list2.add("e");
// list2.add("3");
// list2.add("4");
// System.out.println(list.addAll(3,list2));
// System.out.println(list);
// System.out.println(list.lastIndexOf("b"));
// System.out.println(list.contains("f"));
// System.out.println(list.toString());
// System.out.println(list.containsAll(list2));
// System.out.println(list.set(1,"g"));
System.out.println(list.toString());
list.clear();
System.out.println(list.toString());
// System.out.println(list.addAll(list2));
}
}
写在最后
推荐自己的Github地址:github.com/Lmobject
您的支持与关注是对作者最大的信任,写作容易,坚持不易。谢谢!!!