Java模拟链表操作引发对Java传引用的思考实践

这些天没事在刷刷题,突然遇到一些Java模拟链表的问题,在写的时候也发现了多除与印象中传递引用不同之处,

可能c++基础有半桶水缘故~

先说说Java里面传递参数方法,有传值以及传引用,

传值,就是将基本8数据类型的值传递过去而已

传引用,其实是一种假的传引用,传递的最终是复制一份地址传递过去。

直接说可能太抽象,直接贴出代码慢慢思考:

下面6个test方法,分别会输出怎样的结果呢?

ListNode数据结构:

class ListNode {
	int val;
	ListNode next;

	ListNode(int x) {
		val = x;
	}

	@Override
	public String toString() {
		return "ListNode [val=" + val + ", next=" + next + "]";
	}

}


	private void quoteTest1() {
		ListNode l = new ListNode(2);
		// l2h指向一开始的l,也就是指向l2的头节点
		ListNode lh = l;
		for (int i = 0; i < 6; i++) {
			l.next = new ListNode(1);
			// 注意此时,l不为null,l的next才为null
			l = l.next;
		}
		System.out.println("lh is:" + lh);
		System.out.println("l is:" + l);
	}

	
	private void quoteTest2() {
		ListNode l = null;
		// l2h指向一开始的l,也就是指向l2的头节点
		ListNode lh = l;
		l = new ListNode(2);
		for (int i = 0; i < 6; i++) {
			l.next = new ListNode(1);
			// 注意此时,l不为null,l的next才为null
			l = l.next;
		}
		System.out.println("lh is:" + lh);
		System.out.println("l is:" + l);
	}

	
	private void quoteTest3() {
		ListNode l = new ListNode(2);
		boolean firstInitialize = false;
		ListNode lh = null;
		// l2h指向一开始的l,也就是指向l2的头节点
		if (!firstInitialize) {
			lh = l;
			firstInitialize = true;
		}

		for (int i = 0; i < 6; i++) {
			l.next = new ListNode(1);
			// 注意此时,l不为null,l的next才为null
			l = l.next;
		}
		System.out.println("lh is:" + lh);
		System.out.println("l is:" + l);
	}

	
	private void quoteTest4() {
		ListNode l = null, lh = null;
		boolean firstInitialize = false;
		for (int i = 0; i < 6; i++) {
			// 执行之前,l为null。
			l = new ListNode(2);
			// l2h指向一开始的l,也就是指向l2的头节点,这段代码单线程下会保证只会执行一次嘛
			if (!firstInitialize) {
				lh = l;
				firstInitialize = true;
			}
			// l不为null,但是l.next为null,所以下一次l又为null了。
			l = l.next;
		}
		System.out.println("lh is:" + lh);
		System.out.println("l is:" + l);
	}

	
	private void quoteTest5() {
		ListNode l = new ListNode(1), lh = new ListNode(2);
		// 传递l和lh的引用,事实证明传的是地址的复制,l和l2完全没有改变
		quoteHelp1(lh, l);
		System.out.println("lh is:" + lh);
		System.out.println("l is:" + l);
	}

	/**
	 * 仅用于给quoteTest5来使用的工具方法类 里面的业务代码和 quoteTest4 一模一样
	 * 
	 * @param lh
	 * @param l
	 */
	private void quoteHelp1(ListNode lh, ListNode l) {
		boolean firstInitialize = false;
		for (int i = 0; i < 6; i++) {
			// 执行之前,l为null。
			l = new ListNode(2);
			// l2h指向一开始的l,也就是指向l2的头节点,这段代码单线程下会保证只会执行一次嘛
			if (!firstInitialize) {
				lh = l;
				firstInitialize = true;
			}
			// l不为null,但是l.next为null,所以下一次l又为null了。
			l = l.next;
		}
	}

	private void quoteTest6() {
		ListNode l = new ListNode(3);
		l = quoteHelp2();
		System.out.println("l is:" + l);
	}

	/**
	 * quoteTest6的工具方法
	 * 
	 * @return
	 */
	private ListNode quoteHelp2() {
		
		ListNode l = new ListNode(2);
		// l2h指向一开始的l,也就是指向l2的头节点
		ListNode lh = l;
		for (int i = 0; i < 6; i++) {
			l.next = new ListNode(1);
			// 注意此时,l不为null,l的next才为null
			l = l.next;
		}
		return lh;
	}

慢慢分析:

quoteTest1方法输出:

lh is:ListNode [val=2, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=null]]]]]]]
l is:ListNode [val=1, next=null]

按照正常逻辑去思考,即链表,lh指向这条链表的头节点,最终输出就可以了,由输出可知,是连起来的。


quoteTest2方法输出:

lh is:null
l is:ListNode [val=1, next=null]

由lh只是在最开始的时候赋值了l,但是,此时l也为null,所以其实lh为null,并没有跟着l指向其下一块地址,所以也不存在会是此链表的头节点了。


quoteTest3方法输出:

lh is:ListNode [val=2, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=null]]]]]]]
l is:ListNode [val=1, next=null]

quoteTest3内部逻辑代码和quoteTest1其实是相同的,只是增加了一次判断赋值,所以输出基本都是和quoteTest1相同


quoteTest4方法输出:

lh is:ListNode [val=2, next=null]
l is:null

这个测试方法我感觉是最纠结的,很像quoteTest1方法,和quoteTest3方法也有点相似,但是输出却与他们截然不同,
很明显,lh在值上仅仅等于头节点,但是头节点,却没有和后面的节点连起来????

上面分析看似比较符合逻辑,一开始我也是这样分析的,然后有个致命的地方,l每次都是null,对l每次循环都是null,

既然是null,你又如何能够用next指向它呢?用next去指向一个null?肯定是不对的。

这也就导致了,这条链根本连不起来,从第二个开始就是脱节了的,每一个都是游离在内存中的ListNode节点。

因为lh只赋值了一次给头节点,所以lh当然只指向了这个头节点。


quoteTest5方法输出:

lh is:ListNode [val=2, next=null]
l is:ListNode [val=1, next=null]

由输出可知,l和lh完全没有改变过,假设在quoteTest5方法里面的l和lh分别命名为lTemp和lhTemp的话,其实只要一命名就估计讲清楚了。

看一张简单内存分析图:

这里为了好区分,就把l改成大写的L了,LTemp最终传入方法里面式,是指向的L原来指向的那个内存空间地址,

但是L后面被new了,也就是说它重新指向了,可以假想为指向了下面的长方形内存块。

java里面传引用,也是传值,传的是地址空间块地址。如果此时仅仅对LTemp进行修改操作如:

LTemp.val=9;

那么最终是更改了椭圆块的内容,即实际L指向的内容。


quoteTest6的输出:

l is:ListNode [val=2, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=ListNode [val=1, next=null]]]]]]]

即最终,l的值是改变了,因为由方法返回之前,把lh的引用块赋值给了l,即l指向了lh指向的内存块地址。


水平有限,如有不对,希望不吝指出!








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值