从手写LinkedList看底层代码实现结构



代码已实现的方法:
方法名作用备注
clear()初始化链表&清空链表两个作用
size()取链表容量
isEmpty()判断链表是否为空
add()往链表中插入一个值多个构造方法,含私有方法
set()修改链表指定位置元素的值同上
get()取索引位置元素值同上
remove()删除索引元素同上
iterator()取链表迭代器
LinkedList相关知识科普:
  • LinkedList可以动态添加、删除、修改、查找容器中的元素。因此要使得我们的LinkedList具有最基本的自理能力,必须要实现以上增删改查方法。

  • 在java容器中,其底层实现方式为双端链表。因此其初始(容器为空)结构为:首节点与尾结点相互连接,首节点下一个节点为尾结点,尾结点上一个节点为首节点。

  • LinkedList中的迭代器,被用于快速遍历LinkedList结构的元素,其优点在于LinkedList内部的结构对调用者是透明的,无需关心结构,便可以拿到数据。省去了开发人员自己探索LinkedList的结构过程。

  • 迭代器内部结构说明:除了以下三个方法,还有三个属性:

    • 用于记录当前扫描到的节点:(Node)currentNode;
    • 用于记录从双端链表生成以来对链表长短修改的次数:(Int)expectedModCount,值得说明的是,这个检测是用来防止迭代器迭代过程中程序对链表长短的修改,具体实现方法是这样的,在LinkedList中有个modCount,每当用户增加或删除节点操作时,modCount都会+1,用户取用迭代器时,会为迭代器的expectedModCount赋值为modCount,使用迭代器next取值时,便会检查expectedModeCount与modCount是否相同,不相同时,说明链表结构被修改,接下来读出的值有可能是已经读过的,导致取值错误,因此此时会抛出ConcurrentModificationException异常;
    • 用于记录当前状态是否可以执行remove方法的(boolean)okToRemove, 此值默认为false, 会在next方法执行过程中更改为true,表示当前节点有prev节点,可以删除prev节点。
  • 迭代器主要实现三个方法:

方法名作用备注
hasNext检测当前节点是否为尾结点实现方法:通过判断currentNode是否是尾结点
next读取返回当前节点的值并指向下一个节点需要先检验:1.hasNext检验;2.迭代器expectedModCount与LinkedList中modCount值是否一致
remove从双端队列中删除当前节点需要先检验:1同next方法第二条检测.;2. okToRemove是否为true;
收获:
  • 内部类
    • 内部类可以分为静态与非静态,其定义和创建方法如下:
    • 在这里插入图片描述
    • 通过使用静态类,可以更方便类的创建。
  • 泛型运用
    • 定义节点Node<T>,也可以写成Node<AnyType>,此写法表示该结构内部可以容纳任何对象,从而起到容器支持任何对象的泛化能力。
    • 我们对其抽象时,可以认为在含有泛型的对象或者方法中,其可以处理任意对象即可。
  • 类限定词运用
    • 在本实例中,将需要提供给用户的接口使用public定义,而实现public方法的工具类使用private定义,保证最原始的实现方法不对外部公开。
  • 方法调用设计
    • 这里我们关注方法的返回值,在增删改查功能方法中,增加元素时,不需要返回值,其他三类操作都需要返回对应的数据,如删除数据时,应将被删除的数据返回,修改数据时,应将被修改的数据返回。
  • 缩短链表扫描时间方法
    • 根据索引寻找节点时,当被寻找元素索引在[0,size()/2]区间,则从左向右扫描,否则从右向左扫描。
  • 类中方法设计遵守的原则
    • 对外提供的接口使用public限定,内部实现public方法的辅助方法使用private限定保证对外隐藏。
    • 由private实现同种功能的一次实现,其他衍生出的重载方法可在private的方法之上计算参数,并传入private完成指定功能,例如:add方法可以按照索引插入,也可以在某个元素之前插入,因此底层private需要定义为在某位置插入某个元素,此时按照索引插入的方法可以自行计算好需要插入的元素位置,然后调用在某个元素之前插入,再由private执行具体的插入方法。
  • 内部类如何调用其父类方法
    • 内部类中调用父类的方法时IDE会报错找不到该方法,假如外部类为MyLinkedLIst,需要调用的外部类中的方法名remove(Node<T>),则在内部类的方法中使用:MyLinkedList.this.remove(xx);
  • 并发安全检测
    • 在LinkedList中,使用modCount来记录从链表创建开始,对链表长度修改的次数(增长或缩短都会对modCount加一),当用户调用获取LinkedLIst迭代器的后,会将modCount的值赋予迭代器中的expectedModCount,每次迭代器进行next或者remove操作时都会检测LinkedList中的modCount和自己expectedModCount的值是否一致,一致则执行相应操作,此举目的在于,防止迭代器取值过程中,外部对该链表长度进行修改从而使得迭代器取值错误。
  • 迭代器实现原则
    • 迭代器的意义在于,使得用户不再关心数据链的结构从而快速从某结构中获取数据,节省用户自行遍历时对数据结构的探索。
    • 迭代器需要实现三个功能:1.hasNext:用于检测当前节点是否为尾结点;2. next:用于取出当前节点的值并将节点指针指向下一个节点;3.remove:用于删除上一个节点。
  • 类的属性及方法属性初值可否为空问题
    • jvm会对类的属性赋初值,但是不会对类方法中的局部变量赋初值,因此若局部变量定义时,未赋初值则IDE会报错。
思考
  • 关于代码结构:MyLinkedList设计思想中,与经典MVC结构类似,或者与视图计算层分离思想类似(通过设计缓冲队列,视图层月计算层分别面向缓冲队列数据进行展示和计算),将private作为最底层的真正的逻辑实现,通过多种构造方法相互计算之间的参数,实现一个private方法的多种条件计算。
代码附录
  • 代码区:
    • 测试代码:
package DatastructureImplement;

import java.util.Iterator;

public class Playground {
	public static <E> void main(String[] args) {
		/**
		 * test:
		 * add()
		 * get()
		 */
		MyLinkedList<Integer> mld = new MyLinkedList<Integer>();
		for(int i = 0;i < 10;i++){
			mld.add(i);
		}
		for(int i = 9;i>=0;i--){
			System.out.println(mld.get(i));
		}
		/**
		 * test:
		 * set()
		 * remove()
		 */
		System.out.println("-------------");
		System.out.println(mld.set(9, 99)); //9
		System.out.println(mld.remove(9)); //99
		System.out.println(mld.toString());
		
		/**
		 * iterator test
		 */
		Iterator<Integer> iterator = mld.iterator();
		System.out.println("=================");
		while(iterator.hasNext()){
			System.out.println(iterator.next());
			iterator.remove();
		}
		System.out.println("-=-=-=-=-=-=-=-=-=");
		System.out.println(mld.toString());
	}
}

    • MyLinkedList实现代码:
package DatastructureImplement;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class MyLinkedList<T> implements Iterable<T>{
	private static class Node<T>{
		public Node(T d,Node<T> p,Node<T> n) {
			// TODO Auto-generated constructor stub
			data = d;
			prev = p;
			next = n;
		}
		public T data;
		public Node<T> prev;
		public Node<T> next;
	}
	public MyLinkedList() {
		// TODO Auto-generated constructor stub
		clear();
	}
	public void clear(){doClear();}
	
	private void doClear(){
		beginMarker = new Node<T>(null, null, endMarker);
		endMarker = new Node<T>(null, beginMarker, null);
		theSize = 0;
		modCount++;
	}
	/**
	 * clear() -x
	 * size()  -x
	 * isEmpty()-x
	 * add() -> addBefore  -x
	 * set() -x
	 * get() -x
	 * remove() -x
	 * 
	 * Utils method:
	 * 	getNode() -x
	 * 		getNode(int idx)
	 * 		getNode(int idx, int lower, int upper)
	 * 	remove() -x
	 * 		remove(int idx)
	 * 		remove(Node<T> node)
	 */
	public int size(){return theSize;}
	public boolean isEmpty(){return size()==0;}

	public T remove(int idx){
		return remove(getNode(idx));
	}
	public T remove(Node<T> node){
		node.next.prev = node.prev;
		node.prev.next = node.next;
		modCount++;
		theSize--;
		return node.data;
	}
	public T get(int idx){
		return getNode(idx).data;
	}
	public T set(int idx, T newVal){
		Node<T> node = getNode(idx);
		T oldVal = node.data;
		node.data = newVal;
		return oldVal;
	}
	public void add(T data){
		addBefore(getNode(size()),data);
	}
	public void addBefore(Node<T> node,T data){
		Node<T> newNode = new Node<T>(data, node.prev, node);
		node.prev.next = newNode;
		node.prev = newNode;
		theSize++;
		modCount++;
	}
	private Node<T> getNode(int idx){
		return getNode(idx,0,size());
	}
	private Node<T> getNode(int idx, int lower, int upper){
		Node<T> retNode; //成员变量默认有初值,而成员方法中的局部变量没有初值。因此如果不赋值直接返回ide会报错。
		// idx regular check
		if(idx < lower || idx > upper) throw new IndexOutOfBoundsException();
		
		if(idx < size()/2){
			retNode = beginMarker.next;
			for(int i = lower; i < idx;i++){
				retNode = retNode.next;
			}
		}else{
			retNode = endMarker;
			for(int i = size();i > idx;i--){
				retNode = retNode.prev;
			}
		}
		return retNode;
	}
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		for(int i = 0;i<theSize;i++){
			if(i != 0){
				sb.append(",");
			}
			sb.append(get(i)+"");
		}
		return sb.toString();
	}
	private int theSize;
	//记录对链表所作的改变次数,用于与迭代器中modCount匹配,当失配时抛出ConcurrentModificationException
	//其想法在于,当一个迭代器被创建时,它将存储集合的modCount, 使用modCount来判断是否并发安全。
	private int modCount = 0; 
	private Node<T> beginMarker;
	private Node<T> endMarker;
	@Override
	public Iterator<T> iterator() {
		return new MyLinkedListIterator();
	}
	private class MyLinkedListIterator implements Iterator<T>{

		private Node<T> currentNode = beginMarker.next;
		private int expectedModCount = modCount;
		private boolean okToRemove = false;
		@Override
		public boolean hasNext() {
			// TODO Auto-generated method stub
			return currentNode != endMarker; 
		}
		@Override
		public T next() {
			// TODO Auto-generated method stub
			modCountCheck();
			if(!hasNext()) throw new NoSuchElementException();
			T nextItem = currentNode.data;
			currentNode = currentNode.next;
			okToRemove = true;
			return nextItem;
		}
		@Override
		public void remove() {
			// TODO Auto-generated method stub
			modCountCheck();
			if(!okToRemove) throw new IllegalStateException();
//			remove(currentNode.prev);
			MyLinkedList.this.remove(currentNode.prev); //this表示一个实例
			okToRemove = false;
			expectedModCount++;
		}
		private void modCountCheck(){
			if(expectedModCount != modCount) throw new ConcurrentModificationException();
		}
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值