Java学习笔记(37)—— List接口(ArrayList与LinkedList)

这里将自己学习java及其应用的一些笔记、积累分享一下,如果涉及到了文章、文字侵权,请联系我删除或调整。


一、LinkedList

1.1 概述

  • 双向列表,两端操作效率高

1.2 方法

  • add(数据)
  • add(i, 数据)
  • get(i)
  • remove(i)

移除指定位置数据,返回被移除的值

  • remove(数据)

找到第一个相等数据移除

返回布尔值,表示是否找到数据并移除

  • size()

元素的数量

  • iterator()

辅助创建迭代器对象的方法。通过下标遍历双向链表,效率低;通过迭代器遍历双向链表,效率高。

迭代器实例.hasNext(),判断是否有下一个元素;

迭代器实例.Next(),获取下一个元素;

  • addFirst(), addLast()
  • getFirst(), getLast()
  • removeFirst(), removeLast()

1.3 队列Queue,FIFO

基于LinkedList实现的队列具备以下方法:

  • offer(), 等同于addLast()
  • peek(), 等同于getFirst()
  • poll(), 等同于removeFirst()

1.4 栈 Stack,LIFO

基于LinkedList实现的栈具备以下方法:

  • push(), 等同于addFirst()
  • pop(), 等同于removeFirst()

​​​​​​​1.5 练习:双向链表

package 练习LinkedList;

import java.rmi.Remote;
import java.util.Iterator;
import java.util.LinkedList;import javax.print.attribute.Size2DSyntax;

/*
 *	双向链表 
 */
public class Test1 {
	public static void main(String[] args) {
		LinkedList<String> list = new LinkedList<String>();
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		list.add("eee");
		list.add("fff");
		list.add("ggg");
		System.out.println("链表的数据元素个数:"+list.size());
		System.out.println(list); // 默认toString
		System.out.println("链表的第一个数据元素:"+list.get(0));
		System.out.println("链表的最后一个数据元素:"+list.get(list.size()-1));
		System.out.println("链表删除下标2的返回值(即被删除值):"+list.remove(2));
		System.out.println("链表删除下标2后的的数据元素:"+list);
		System.out.println("链表删除“aaa”的返回值(boolean值):"+list.remove("aaa"));
		System.out.println("链表删除“aaa”后的的数据元素:"+list);
	
		// 双向链表下标遍历效率低
		for(int i = 0; i < list.size();i++) {
			System.out.printf("%s, ",list.get(i));
		}
		
		// 迭代器遍历链表效率高
		/*	迭代器有两个方法:
		 * 	next()
		 * 	hasnext()
		 * 迭代器不能自己创建对象,只能通过类来辅助创建对象 
		 */
		
		// 新建list的迭代器对象,迭代器对象是Iterator<E>类型
		Iterator<String> it = list.iterator();
		while(it.hasNext()) {
			System.out.printf("%s, ",it.next());
		}
	}	
}

1.6 练习:双向链表的下标遍历和迭代器遍历

package 练习LinkedList;
/*
 *	双向链表的下标遍历与迭代器遍历对比 
 */
import java.util.Iterator;
import java.util.LinkedList;

public class Test2 {
	public static void main(String[] args) {
		/*
		 *	Integer.valueOf(1),这一个对象重复放入双向列表10万次 
		 */
		// 创建双向链表list
		LinkedList<Integer> list = new LinkedList<Integer>();
		
		for(int i = 0;i<100000;i++) {
			list.add(1);
		}
		
		//
		System.out.println("------------下标遍历------------");
		f1(list);
		
		System.out.println("------------迭代器遍历-----------");
		f2(list);
		
	}

	private static void f2(LinkedList<Integer> list) {
		long t = System.currentTimeMillis();
		// 创建迭代器
		Iterator<Integer> it = list.iterator();
		// 迭代器遍历 
		while(it.hasNext()) {
			it.next();
		}
		// 计算遍历时间
		t = System.currentTimeMillis()-t;
		System.out.println(t);
	}

	private static void f1(LinkedList<Integer> list) {
		long t = System.currentTimeMillis();
		// 下标遍历
		for(int i =0;i<list.size();i++) {
			list.get(i);
		}
		// 计算遍历时间
		t = System.currentTimeMillis()-t;
		System.out.println(t);		
	}
}

1.7 练习:丑数

丑数指的是只包含质因子2,3和5的数。在此,我们要编写一段代码,求第n个丑数。

package 丑数;
/*
 * 第999个丑数 51200000
 */
import java.util.LinkedList;
import java.util.Scanner;

/*
 *	丑数:只包含质因子2,3和5的数称作丑数(Ugly Number) 
 */
public class Test1 {
	public static void main(String[] args) {
		System.out.println("求第几个丑数:");
		int n = new Scanner(System.in).nextInt();
		// 调用方法求取第n个丑数
		long r = f(n);
		System.out.printf("第"+n+"个丑数是:"+r);
	}

	private static long f(int n) {
/*	<tile>丑数计算算法解析</title>
 * ------------------------------ 
 * 2 4 6 8
 * ------------------------------
 * 3 6 9 12
 * ------------------------------
 * 5 10 15 20
 * ------------------------------
 *	2 3 4 5
 *	
 *	1. 准备3个集合,用来存放2,3,5的倍数;
 *	2. 初始状态,先放入2,3,5; 
 * 	      循环{3. 从头部移除最小值,不同集合的等值最小值也移除;
 * 		   4. 最小值分别乘2,3,5,放入3个集合;}
 */
		// 创建3个链表,存放2,3,5的倍数
		LinkedList<Long> list2 = new LinkedList<Long>();
		LinkedList<Long> list3 = new LinkedList<Long>();
		LinkedList<Long> list5 = new LinkedList<Long>();
		// 初始状态,放入2,3,5
		list2.add(2L);
		list3.add(3L);
		list5.add(5L);
		long r = 0;// 用于保存结果
		
		// 从第1个开始,求到第n个为止
		for(int i = 1;i <= n;i++) {
			// 取出最小值并移除
			long a = list2.getFirst();// 涉及自动拆箱
			long b = list3.getFirst();
			long c = list5.getFirst();
			// Math.min(number1, number2)求得两数中的最小数
			r = Math.min(a, Math.min(b, c));// 取出三者的最小值
			
			if(r==a)list2.removeFirst();// 删除三个集合中的最小值
			if(r==b)list3.removeFirst();
			if(r==c)list5.removeFirst();
			
			// r 乘2,3,5放入集合
			list2.add(r*2);
			list3.add(r*3);
			list5.add(r*5);
		}
		// 返回第n个丑数
		return r;
	}
}

1.8 练习:自己手写双向列表

package 自己手写双向列表;

public class SXLianBiao<T> {
	private Node first;// 为了体现双向列表头尾访问的高效性,添加头节点与尾节点 
	private Node last;// 初始为null
	private int size;// 链表中数据的数量
	
	// 添加数据
	public void add (T value) {
		Node tmp = new Node();
		tmp.value = value;// 数据封装入节点
		
		// 链表为空,无数据
		if(this.size==0) {
			tmp.prev = tmp;
			tmp.next = tmp;
			first = tmp;
			last = tmp;
		}else {// 链表不为空,已经有数据
			tmp.prev = last;
			last.next = tmp;
			tmp.next = first;
			first.prev = tmp;
			
			last = tmp;// 修改完对应的指针后,更新last节点为新添加的节点
		}
		this.size++;
	}

	// 获取链表数据
	public T get (int i) {
		// i越界,亦或没有数据,则无法访问
		// 取头尾部数据
		// 取中间位置的节点
		Node tmp = getNode(i);
		return tmp.value;
	}
	
	private SXLianBiao<T>.Node getNode(int i) {
		// i越界,亦或没有数据,则无法访问
		if(i<0 || i >= size) {
			throw new IndexOutOfBoundsException(""+i);
		}
		// 取头尾部数据
		if(i ==0) {
			return first;
		}else if (i == size - 1) {
			return last;
		}
		/* 1 2 3 4 5 6 7 8
		 *         i   
		 */
		// 取中间位置的节点,为了提高访问效率,将双向列表分为两部分分别进行
		if(i < size / 2){// 访问下标i位于列表的前半部分
			Node tmp = first;
			for(int j = 1;j<=i;j++) {
				tmp = tmp.next;
			}
			return tmp;
		}else {// 访问下标i位于列表的前后部分
			Node tmp = last;
			for(int j = size-2;j>=i;j--) {
				tmp = tmp.prev;
			}
			return tmp;
		}
	}

	// 返回链表数据个数
	public int size () {
		return this.size;
	}
	
	// 内部类来封装节点数据
	// Node类,辅助外部双向列表对象来封装局部数据
	private class Node{
		T value;// 节点中封装的数据
		Node prev;// 引用,指向前一个节点
		Node next;// 引用,指向后一个节点
	}
}

1.9 练习:手写双向列表迭代器

package 手写双向链表迭代器;

import java.util.Iterator;
/*
 *	for-each语法,要求欲使用for-each方法的类必须实现Iterable接口
 *	Iterable接口,中包含一个Iterator<T> iterator();抽象方法,必须实现
 */
public class SXLianBiao<T> implements Iterable<T>{
	private Node first;// 为了体现双向列表头尾访问的高效性,添加头节点与尾节点 
	private Node last;// 初始为null
	private int size;// 链表中数据的数量
	
	// 添加数据
	public void add (T value) {
		Node tmp = new Node();
		tmp.value = value;// 数据封装入节点
		
		// 链表为空,无数据
		if(this.size==0) {
			tmp.prev = tmp;
			tmp.next = tmp;
			first = tmp;
			last = tmp;
		}else {// 链表不为空,已经有数据
			tmp.prev = last;
			last.next = tmp;
			tmp.next = first;
			first.prev = tmp;
			
			last = tmp;// 修改完对应的指针后,更新last节点为新添加的节点
		}
		this.size++;
	}

	// 获取链表数据
	public T get (int i) {
		// i越界,亦或没有数据,则无法访问
		// 取头尾部数据
		// 取中间位置的节点
		Node tmp = getNode(i);
		return tmp.value;
	}
	
	private SXLianBiao<T>.Node getNode(int i) {
		// i越界,亦或没有数据,则无法访问
		if(i<0 || i >= size) {
			throw new IndexOutOfBoundsException(""+i);
		}
		// 取头尾部数据
		if(i ==0) {
			return first;
		}else if (i == size - 1) {
			return last;
		}
		/* 1 2 3 4 5 6 7 8
		 *         i   
		 */
		// 取中间位置的节点,为了提高访问效率,将双向列表分为两部分分别进行
		if(i < size / 2){// 访问下标i位于列表的前半部分
			Node tmp = first;
			for(int j = 1;j<=i;j++) {
				tmp = tmp.next;
			}
			return tmp;
		}else {// 访问下标i位于列表的前后部分
			Node tmp = last;
			for(int j = size-2;j>=i;j--) {
				tmp = tmp.prev;
			}
			return tmp;
		}
	}

	// 返回链表数据个数
	public int size () {
		return this.size;
	}
	
	// 辅助创建迭代器的方法
	// Iterable接口抽象方法的实现
	public Iterator<T> iterator(){
		return new Itr();// 对象调用该方法时,返回一个对象的迭代器
	}
	
	// 内部类来封装节点数据
	// Node类,辅助外部双向列表对象来封装局部数据
	private class Node{
		T value;// 节点中封装的数据
		Node prev;// 引用,指向前一个节点
		Node next;// 引用,指向后一个节点
	}
	
	// 迭代器类(内部类),封装双向链表的局部运算逻辑
	private class Itr implements Iterator<T>{
		Node tmp = null;// 初始遍历变量为null,同时也便于判断hasNext()
		@Override
		public boolean hasNext() {
			if(size == 0)return false;// 链表无数据,直接返回false
			/*
			 *	遍历变量tmp,初始为null,在第一次next时被赋值first,但
			 *	很快在当前调用轮回中,又被first.next覆盖;因此,当tmp又
			 *	取值为first时,tmp已依次遍历链表
			 */ 
			if(tmp != first)return true;
			return false;
		}

		@Override
		public T next() {
			if(tmp == null) {
				tmp = first;
			}
			T value = tmp.value;// 取当前节点的value存入临时变量,用于返回
			tmp = tmp.next;// 取值后,迭代器tmp更新为下一个节点
			return value;
		}
	}
}
package 手写双向链表迭代器;

import java.util.Iterator;

public class Test1 {
	public static void main(String[] args) {
		SXLianBiao<String> list = new SXLianBiao<String>();
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		list.add("ddd");
		
		System.out.println(list.size());
		System.out.println(list.get(1));
		System.out.println(list.get(list.size()-2));
		
		// 创建迭代器,因为迭代器对象是由iterator方法在类内部new的,所以此处仅调用方法即可
		Iterator<String> it = list.iterator();
		System.out.println(it);
		while(it.hasNext()) {
			System.out.println(it.next());
		}
		
		// 基于迭代器,使用for-each方法遍历
		for(String w : list) {
			System.err.println(w);
		}
	}	
}

二、ArrayList和LinkedList

2.1 Collection 接口

  • List 接口

ArrayList

LinkedList

  • ArrayList

访问任意位置效率高

增删数据,效率可能降低

  • LinkedList

两端操作效率高

  • 如果仅在两端操作数据,使用 LinkedList
  • 当数据量较小时(<10),频繁增删数据,使用LinkedList
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值