ArrayList和LinkedList的区别

要想知道ArrayList和LinkedList的区别,首先我们要知道什么是ArrayList和LinkedList。

ArrayList

1、ArrayList的概念

ArrayList是一个可以处理变长数组的类型,这里不局限于“数”组,ArrayList是一个泛型类,可以存放任意类型的对象。顾名思义,ArrayList是一个数组列表,因此其内部是使用一个数组来存放对象的,实现了List接口的容器类。因为是数组,所以一旦创建,就不能轻易改变其长度。因此他的查询快和修改快,增、删慢。

2、常用方法

ArrayList类常用的方法有add()、clear()、get()、indexOf()、remove()、sort()、toArray()、toString()等等,同时ArrayList内部有一个私有类实现Iterator接口,因此可以使用iterator()方法得到ArrayList的迭代器,同时,还有一个私有类实现了ListIterator接口,因此ArrayList也可以调用listIterator()方法得到ListIterator迭代器。
另外,由于ArrayList的所有方法都是默认在单一线程下进行的,因此ArrayList不具有线程安全性。若想在多线程下使用,应该使用Colletions类中的静态方法synchronizedList()对ArrayList进行调用即可。
下面是模仿底层的代码
设计类

package cn.itsource.arraylist2;

import java.util.Arrays;

/**
 * 基于数组,适用于查改。
 * 增删插效率低下
 *				1. 请设计一个类ArrayListDemo,可以装多个不同类型的数据,初始长度是10个
 *					底层是基于数组,因为是可以装不同数据类型的数据,所以是Object数组
 *					int/byte/short /boolean....
 *					Object[] value = new Object[10];
 *					value[0] = 1;//int 的1装好为对应的包装类Integer
 *
 *				2. 可以记录当前数据的个数 int size()
 *					
 *				3. 可以任意添加任意类型的数据 void add(Object obj)
 *						version2 自动扩容
 *
 *				4. 可以打印当前数据结构的对象,格式如下:[值1,值2.。。。。]
 *
 *				5. 查询指定下标的元素  Object searchByIndex(int index)
 *				6. 查询指定的元素第一次出现的位置  int searchByElement(Object ele);
 *
 *				7. 修改指定下标处的元素  void updateByIndex(int index,Object obj)
 *				8. 删除指定下标处的元素,返回删除的元素  	Object deleteByIndex(int index)
 *				9. 删除的首次出现的指定元素     void deleteByElement(Object ele)
 *				10.插入指定下标处一个指定元素 void insertByIndex(int index,Object obj)
 *					
 *				11.将对象中所有元素清空 void clear();
 *				12.可以创建指定长度的ArrayList对象
 *				13.创建一个默认长度是10的ArrayList对象
 *				14.创建一个默认长度是10,且有指定元素的ArrayList对象
 */
public class ArrayListDemo {
	/**1. 请设计一个类ArrayListDemo,可以装多个不同类型的数据,初始长度是10个*/
	private Object[] value;
	
	/**元素个数 	2. 可以记录当前数据的个数 int size()*/
	private int size;
	
	/**
	 * 3. 可以任意添加任意类型的数据 void add(Object obj)
	 */
	public void add(Object obj){
		//先判断当前数组是否需要扩容
		growArrayCapcity();
		
		//给数组添加元素
		value[size] = obj;
		
		//元素个数+1
		size++;
	}
	
	/**
	 * 数组扩容方法
	 * 扩容机制 *2
	 */
	private void growArrayCapcity() {
		//先判断当前数组是否可以装下新添加的元素
		if (size == value.length) {//判断当前元素个数是否已经等于当前数组长度了,若等于,说明数组已经装不下了,要扩容
			//数组扩容: 扩容机制,原数组长度*2 
			value = Arrays.copyOf(value, value.length * 2);//必须将新扩容后的数组地址赋值给value
		}
	}
	
	/**
	 * 2. 可以记录当前数据的个数 int size()
	 * @return
	 */
	public int size(){
		return size;
	}
	
	/**
	 * 4. 可以打印当前数据结构的对象,格式如下:[值1,值2.。。。。]
	 */
	@Override
	public String toString() {
		//数组缩容,只要将有元素的数组转换为字符串即可,所以要缩容
		Object[] newArr = Arrays.copyOf(value,size);
		
		String str = Arrays.toString(newArr);//将数组转换为字符串
		return str;
	}
	
	/**
	 * 5. 查询指定下标的元素  Object searchByIndex(int index)
	 * @param index
	 * @return
	 */
	public Object searchByIndex(int index){
		//先校验下标合法性
		checkIndex(index);//alt + shift + m 抽取成一个方法
		return value[index];
	}
	
	/**
	 * 校验下标方法
	 * @param index
	 */
	private void checkIndex(int index) {
		if (index < 0 || index >= size) {
			//抛出非法参数异常,提示一下
			throw new IllegalArgumentException("请注意下标范围,范围是:[0," + (size-1) + "]");
		}
	}
	
 	/**
 	 * 6. 查询指定的元素第一次出现的位置  int searchByElement(Object ele);
 	 * @param ele
 	 * @return
 	 */
	public int searchByElement(Object ele){
		//如果ele是null元素,为了避免空指针一次,用 == 比较
		if (ele == null) {
			//遍历数组,如何用==比较,如果有相等就返回下标
			for (int i = 0; i < size; i++) {//注意,这里必须是装的元素个数,而不是数组长度
				if (ele == value[i]) {//如果相等就返回
					return i;//返回下标
				}
			}
		} else {
			//遍历数组,如何用equals比较,如果有相等就返回下标
			for (int i = 0; i < size; i++) {//注意,这里必须是装的元素个数,而不是数组长度
				if (ele.equals(value[i])) {//如果相等就返回
					return i;//返回下标
				}
			}
		}
		//如果找不到就返回-1
		return -1;//表示没有找到
	}
 
 	/**
 	 * 7. 修改指定下标处的元素  void updateByIndex(int index,Object obj)
 	 * @param index
 	 * @param obj
 	 */
	public void updateByIndex(int index,Object obj){
		//先校验下标合法性
		checkIndex(index);//alt + shift + m 抽取成一个方法
		
		//通过下标修改数组元素
		value[index] = obj;
	}
	
 	/**
 	 * 8. 删除指定下标处的元素,返回删除的元素  	Object deleteByIndex(int index)
 	 * //注意,该方法必须结合画图来实现
 	 * @param index
 	 * @return
 	 */
	public Object deleteByIndex(int index){
		//先校验下标合法性
		checkIndex(index);//alt + shift + m 抽取成一个方法
		
		//声明一个变量保存要删除的元素
		Object obj = value[index];
		
		//注意,该方法必须结合画图来实现
		System.arraycopy(value, index + 1,value,index,size - index -1);//必须结合画图确定复制的元素个数和下标
		
		//删除后元素个数-1
		size--;
		return obj;
	}
	
	/**
	 * 9. 删除的首次出现的指定元素     void deleteByElement(Object ele)
	 * @param ele
	 */
	public void deleteByElement(Object ele){
		//直接调用上面的方法,获取ele的下标,如果下标合法证明有当前元素ele,直接调用上面根据下标删除方法删除即可
		int index = searchByElement(ele);
		//如果index < 0或者大于最大下标,说明当前数组中没有该元素,直接结束方法
		if (index <0 || index > size-1) {
			return;
		}
		
		//调用根据下标删除方法
		deleteByElement(index);
	}
	
	/**
	 * 10.插入指定下标处一个指定元素 void insertByIndex(int index,Object obj)
	 * 也要考虑3种情况,首、尾和中间
	 * 也要结合画图,参考删除方法
	 * 图见课堂画图
	 * @param index
	 * @param obj
	 */
	public void insertByIndex(int index,Object obj) {
		//因为数组下标有可能越界,所以要校验合法性,注意,这里特殊处理,因为允许在size处插入元素,所以判断条件要改一下
		if (index < 0 || index > size){
			throw new IllegalArgumentException("亲,请注意,只允许插入下标范围是:[0," + size + "]之内的元素");
		}
		
		//先将特殊情况单独处理,即下标 是size的时候,相当于直接在添加元素,所以直接调用add方法
		if (index == size) {
			add(obj);
			return;//添加完毕后,必须结束方法
		}
		
		//先判断是否需要扩容,这里因为在add方法中也有扩容需求,所以将扩容功能抽取为一个方法growArrayCapcity
		growArrayCapcity();
		
		//画图 搞清楚需要复制几个元素和下标的几个特殊情况:第一个、最后一个。注意:必须先复制再给指定下标元素赋值。
		System.arraycopy(value, index, value, index+1, size-index);
		//给指定下标元素赋值
		value[index] = obj;
		//元素个数+1
		size++;	
	}	

	/**
	 * 11.将对象中所有元素清空 void clear();
	 */
	public void clear(){
		//循环遍历,重新赋值null/Arrays.fill()
		
		//将vlaue重新赋值即可
		value = new Object[10];//没有充分调用GC
		//元素个数重置为0
		size = 0;
		//扩展:遍历数组将每一个元素赋值为null,可以提高GC回收能力,节约资源
	}
	
	/**
	 * 12.可以创建指定长度的ArrayList对象
	 * @param length
	 */
	public ArrayListDemo(int length) {
		super();
		value = new Object[length];
	}
	
	/**
	 * 13.创建一个默认长度是10的ArrayList对象
	 */
	public ArrayListDemo() {
		this(10);
	}
	
	/**
	 * 14.创建一个默认长度是10,且有指定元素的ArrayList对象
	 */
	public ArrayListDemo(Object obj) {
		this(10);
		//直接都用添加元素方法
		add(obj);
	}
}

测试类

package cn.itsource.arraylist2;

/**
 *	测试ArrayListDemo类
 */
public class ArrayListDemoTest {
	public static void main(String[] args) {
		// 创建对象
		ArrayListDemo list = new ArrayListDemo();

		// 3. 可以任意添加任意类型的数据 void add(Object obj)
		list.add(1);//实际添加的是Integer类型的1
		list.add(true);
		list.add(3.14);
		list.add("黄瓜");
		list.add("狼牙棒");
		list.add(null);
		

		// 2. 获取当前数据的个数 int size()
		System.out.println(list.size());

		// 4. 可以打印当前数据结构的对象,格式如下:[值1,值2.。。。。]
		System.out.println(list);

		// 5. 查询指定下标的元素 Object searchByIndex(int index)极限测试
		Object searchByIndex = list.searchByIndex(5);
		System.out.println(searchByIndex);

		// 6. 查询指定的元素第一次出现的位置 int searchByElement(Object ele);
		int searchByElement = list.searchByElement("娃娃");
		System.out.println(searchByElement);

		// 7. 修改指定下标处的元素 void updateByIndex(int index,Object obj)
		list.updateByIndex(3, "蜡烛");
		System.out.println(list);
		
		// 8. 删除指定下标处的元素,返回删除的元素 Object deleteByIndex(int index) 必须结合画图
		Object deleteByIndex = list.deleteByIndex(3);
		System.out.println("要删除的元素:" + deleteByIndex);
		System.out.println(list);
		
		// 剩余方法自己测试

	}

}

LinkedList

1、概念

LinkedList是一个链式结构,他可以分为单向链表、双向链表、循环链表。当然它实现了Deque接口和List接口。同样,LinkedList也是线程不安全的,如果在并发环境下使用它,同样用Colletions类中的静态方法synchronizedList()对LinkedList进行调用即可。在LinkedList的内部实现中,并不是用普通的数组来存放数据的,而是使用结点来存放数据的,有一个指向链表头的结点first和一个指向链表尾的结点last。不同于ArrayList只能在数组末尾添加数据,LinkList可以很方便在链表头或者链表尾插入数据,或者在指定结点前后插入数据,还提供了取走链表头或链表尾的结点,或取走中间某个结点,还可以查询某个结点是否存在。add()方法默认在链表尾部插入数据。总之,LinkedList提供了大量方便的操作方法,并且它的插入或增加等方法的效率明显高于ArrayList类型,但是查询的效率要低一点,因为它是一个双向链表。

2、方法

增加:
add(E e):在链表后添加一个元素; 通用方法
addFirst(E e):在链表头部插入一个元素; 特有方法
addLast(E e):在链表尾部添加一个元素; 特有方法
push(E e):与addFirst方法一致
offer(E e):在链表尾部插入一个元素
add(int index, E element):在指定位置插入一个元素。
offerFirst(E e):JDK1.6版本之后,在头部添加; 特有方法
offerLast(E e):JDK1.6版本之后,在尾部添加; 特有方法

删除:
remove() :移除链表中第一个元素; 通用方法
remove(E e):移除指定元素; 通用方法
removeFirst(E e):删除头,获取元素并删除; 特有方法
removeLast(E e):删除尾; 特有方法
pollFirst():删除头; 特有方法
pollLast():删除尾; 特有方法
pop():和removeFirst方法一致,删除头。
poll():查询并移除第一个元素 特有方法

查:
get(int index):按照下标获取元素; 通用方法
getFirst():获取第一个元素; 特有方法
getLast():获取最后一个元素; 特有方法
peek():获取第一个元素,但是不移除; 特有方法
peekFirst():获取第一个元素,但是不移除;
peekLast():获取最后一个元素,但是不移除;
pollFirst():查询并删除头; 特有方法
pollLast():删除尾; 特有方法
poll():查询并移除第一个元素 特有方法

下面是模拟底层实现的方法
设计类

package cn.itsource.linkedlist2;

/**
 *	基于变量(对象):可以保存多个不同类型的数据
 *			
 *				1. 可以任意添加任意类型的数据 void add(Object obj)
 *				2. 可以记录当前数据的个数 int size()
 *				3. 可以打印当前数据结构的对象 toString(),格式如下:[值1,值2.。。。。]
 *
 *				根据下标查询当前的节点对象 Node searchNodeByIndex(int index)(重要方法)
 *				
 *				4. 查询指定下标的元素  Object searchByIndex(int index)
 *				5. 查询指定的元素第一次出现的位置  int searchByElement(Object ele);
 *
 *				6. 修改指定下标处的元素  void updateByIndex(int index,Object obj)
 *				7. 删除指定下标处的元素,返回删除的元素  	Object deleteByIndex(int index)
 *				8. 删除的首次出现的指定元素     void deleteByElement(Object ele)
 *				9. 插入指定下标处一个指定元素 void insertByIndex(int index,Object obj)
 *					
 *				10.将对象中所有元素清空 void clear();
 *				11.创建对象,可以直接传入一个参数初始化
 */
public class LinkedListDemo{
	/**保存第一个添加的元素地址*/
	private Node frist;
	
	/**添加的元素个数*/
	private int size;
	
	
	/**
	 * 1. 可以任意添加任意类型的数据 void add(Object obj)
	 */
	public void add(Object obj){
		//创建新的对象
		Node node = new Node(obj);
		//判断是否是第一元素,如果是就把第一个元素赋值
		if (frist == null) {
			frist = node;
		}else {
			//从第一个元素开始遍历,创建临时变量
			Node temp = frist;
			while (temp.next != null) {
				temp = temp.next;
			}
			temp.next = node;
		}
		size++;
	}

	/**
	 * 2. 可以记录当前数据的个数 int size()
	 * @return
	 */
	public int size() {
		return size;
	}
	
	/**
	 * 3. 可以打印当前数据结构的对象 toString(),格式如下:[值1,值2.。。。。]
	 */
	@Override
	public String toString() {
		//创建StringBulider对象
		StringBuilder builder = new StringBuilder("[");
		//判断是否是有元素,乳沟没有元素这返回[]
		if (frist == null) {
			return builder.append("]").toString();
		}else {
			//循环遍历拼接字符串
			//创建一个对象来储存第一个元素
			Node node = frist;
			builder.append(node.value);
			for (int i = 0; i < size-1; i++) {
				builder.append(",");
				node = node.next;
				builder.append(node.value);
			}
		}
		return builder.append("]").toString();
	}
	/**
	 * 根据下标查询当前的节点对象 Node searchNodeByIndex(int index)
	 * @param index
	 * @return
	 */
	public Node searchNodeByIndex(int index) {
		checkIndex(index);
		Node node = frist;
		for (int i = 0; i < index; i++) {
			node = node.next;
		}
		return node;
	}
	/**
	 * 校验链表下标方法
	 * @param index
	 */
	private void checkIndex(int index) throws IllegalArgumentException {
		if (index > size-1 || index < 0) {
			throw new IllegalArgumentException("请注意下标是否越界,范围是[0,"+(size-1)+"]");
		}
	}

	/**
	 * 4. 查询指定下标的元素  Object searchByIndex(int index)
	 * @param index
	 * @return
	 */
	public  Object searchByIndex(int index) {
		Node node = searchNodeByIndex(index);
		return node.value;
	}
	
	/**
	 * 5. 查询指定的元素第一次出现的位置  int searchByElement(Object ele);
	 * @param ele
	 * @return
	 */
	public int searchByElement(Object ele) {
		Node node = frist;
		if (ele == null) {
			for (int i = 0; i < size; i++) {
				if (ele == node.value) {
					return i;
				}
			}
		}else {
			for (int i = 0; i < size; i++) {
				if (ele.equals(node.value)) {
					return i;
				}
				node = node.next;
			}
		}
		return -1;
	}

	/**
	 * 6. 修改指定下标处的元素  void updateByIndex(int index,Object obj)
	 * @param index
	 * @param obj
	 * @throws IllegalArgumentException 
	 */
	public void updateByIndex(int index,Object obj) {
		checkIndex(index);
		Node byIndex = searchNodeByIndex(index);
		byIndex.value = obj;
	}
	/**
	 * 7. 删除指定下标处的元素,返回删除的元素  	Object deleteByIndex(int index)
	 * @param index
	 * @return
	 * @throws IllegalArgumentException
	 */
	public Object deleteByIndex(int index) {
		checkIndex(index);
		Node obj = null;
		if (index == 0) {
			obj = frist;
			frist = frist.next;
		}else {
			Node lastindex = searchNodeByIndex(index-1);
			obj = searchNodeByIndex(index);
			lastindex.next = obj.next;
		}
		size--;
		return obj.value;
	}

	/**
	 * 8. 删除的首次出现的指定元素     void deleteByElement(Object ele)
	 * @param ele
	 * @throws IllegalArgumentException 
	 */
	public void deleteByElement(Object ele) {
		int index = searchByElement(ele);
		if (index < 0) {
			System.out.println("没有这个元素");
		}else {
			deleteByIndex(index);
			System.out.println("删除的首次出现的指定元素 :"+this.toString());
		}
		
	}

	/**
	 * 9. 插入指定下标处一个指定元素 void insertByIndex(int index,Object obj)
	 * 跟删除方法类似,通过画图得知,只需要将index上一个元素的next赋值为当前插入的元素地址,将插入的元素next赋值为index处元素的地址即可
	 * @param index
	 * @param obj
	 * @throws IllegalArgumentException 
	 */
	public void insertByIndex(int index,Object obj) throws IllegalArgumentException{
		checkIndex(index);
		if (index < 0 || index > size) {
			throw new IllegalArgumentException("下标越界,请注意下标范围[0,"+size+"]");
		}
		Node node = new Node(obj);
		if (index == 0) {
			node.next = frist;
			frist = node;
		}else if (index == size) {
			add(obj);
			return;
		}else {
			Node nextIndex = searchNodeByIndex(index-1);
			Node now = searchNodeByIndex(index);
			node.next = now;
			nextIndex.next = node;
		}
		size++;
	}
	/**
	 * 10.将对象中所有元素清空 void clear();
	 */
	public void clear() {
		Node node = frist;
		for (int i = 0; i < size; i++) {
			frist = null;
			node.next = frist;
		}
		size = 0;
	}
	/**
	 * 11.创建对象,可以直接传入一个参数初始化
	 * @param obj
	 */
	public LinkedListDemo(Object obj){
		add(obj);
	}
	public LinkedListDemo() {
		super();
	}
	/**
	 * 内部类
	 * 不希望在SinglyLinkedList外部被使用到当前Node类,所以用private修饰
	 */
	private class Node {
		/**用来储存任意类型的值的*/
		Object value;
		/**用来保存下一个元素地址的*/
		Node next;
		/**
		 * 声明有参构造
		 */
		public Node(Object value) {
			this.value = value;
		}
	}
}

测试类

package cn.itsource.linkedlist3;
/**
 * LinkedListDemo测试
 * @author zjj
 *
 */
public class LinkedListTest {

	public static void main(String[] args) {
		//创建LinkedListDemo对象
		LinkedListDemo listDemo = new LinkedListDemo();
		//1. 可以记录当前数据的个数 int size()
//		System.out.println(listDemo.size());
		//2、添加元素并调用toString方法
		listDemo.add("菠萝");
		listDemo.add(123);
		listDemo.add(4);
		listDemo.add('A');
		System.out.println(listDemo.toString());
		//4. 查询指定下标的元素  Object searchByIndex(int index)
		Object value = listDemo.searchByIndex(2);
		System.out.println("查询指定下标的元素为:"+value);
		//	5. 查询指定的元素第一次出现的位置  int searchByElement(Object ele);
		int searchByElement = listDemo.searchByElement('A');
		System.out.println("查询指定的元素第一次出现的位置:"+searchByElement);
		//6. 修改指定下标处的元素 
		listDemo.updateByIndex(2, 321);
		System.out.println("修改指定下标处的元素:"+listDemo);
		//7. 删除指定下标处的元素,返回删除的元素
		listDemo.deleteByIndex(0);
		System.out.println("删除指定下标出的元素:"+listDemo);
		//8. 删除的首次出现的指定元素
		listDemo.deleteByElement(321);
		System.out.println("删除的首次出现的指定元素:"+listDemo);
		//9. 插入指定下标处一个指定元素
		listDemo.insertByIndex(0, 55);
		System.out.println("插入指定下标处一个指定元素:"+listDemo);
		//10.将对象中所有元素清空
		listDemo.clear();
		System.out.println("清空后的元素:"+listDemo);
	}

}

下面是结构图(稍微参考一下就行)
在这里插入图片描述

总结一下

ArrayList底层就是一个可变的数组,其可变是指里面可以储存多个不同类型的元素,因为是数组,所以数组一旦被创建,其长度就不容易被改变,因此对于元素的删除、添加效率是十分低下的,但是查询和修改是十分快速的。而LinkedList是一个链式结构,是靠结点来操作的,需要找到当前元素是需要上一个元素来帮忙的。因此他的查询和更改效率低下,删除和添加与ArrayList相反。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值