这些天没事在刷刷题,突然遇到一些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指向的内存块地址。
水平有限,如有不对,希望不吝指出!