- 问题引入
上图中,赋给b海象的weight会改变a海象的weight,但x的赋值又不会改变y的赋值
Bits
要解释上图的问题,我们应该从Java的底层入手
相同的二进制编码,却因为数据类型不同,输出不同的值
变量的声明
基本类型
Java does not write anything into the reserved box when a variable is declared. In other words, there are no default values. As a result, the Java compiler prevents you from using a variable until after the box has been filled with bits using the = operator. For this reason, I have avoided showing any bits in the boxes in the figure above.
引用类型
When we declare a variable of any reference type (Walrus, Dog, Planet, array, etc.), Java allocates a box of 64 bits, no matter what type of object.
96位大于64位,这似乎有些矛盾:
在Java中:the 64 bit box contains not the data about the walrus, but instead the address of the Walrus in memory.
Gloden rule
练习
答案:B
解析:
- 在调用方法(函数)时,doStuff方法中的int x与main方法中的int x 实际上处于两个不同的scope(调用方法时会new 一个scope,并将main方法中的x变量的位都传递给doStuff方法中的x变量,即值传递),所以x = x - 5实际上只作用于doStuff方法中的x,而不是main方法中的x。
- 但对于引用类型来说,引用类型储存的是引用的地址,所以在进行值传递时传递的是对象的地址,所以doStuff方法中的int x与main方法中的walrus实际上指向相同的一个对象,这使得doStuff中执行的语句会作用于main方法中walrus指向的对象,所以反作用于main方法中的walrus
IntLists
使用递归求链表中元素的个数
java
/** Return the size of the list using... recursion! */
public int size() {
if (rest == null) {
return 1;
}
return 1 + this.rest.size();
}
Exercise: You might wonder why we don't do something like if (this == null) return 0;. Why wouldn't this work?
Answer: Think about what happens when you call size. You are calling it on an object, for example L.size(). If L were null, then you would get a NullPointer error!
不使用递归求元素个数
java
/** Return the size of the list using no recursion! */
public int iterativeSize() {
IntList p = this;
int totalSize = 0;
while (p != null) {
totalSize += 1;
p = p.rest;
}
return totalSize;
}
求第n个元素
SLList
接下来我们对IntList进行改进,使链表看上去不那么naked
有了节点类,现在我们补充上链表类
java
public class SLList {
public IntNode first;
public SLList(int x) {
first = new IntNode(x, null);
}
}
对比SLList和IntList,我们发现,在使用SLList时无需强调null
java
IntList L1 = new IntList(5, null);
SLList L2 = new SLList(5);
addFirst() and getFirst()
java
/** Adds an item to the front of the list. */
public void addFirst(int x) {
first = new IntNode(x, first);
}
/** Retrieves the front item from the list. */
public int getFirst() {
return first.item;
}
private and nested class
java
public class SLList {
public class IntNode {
public int item;
public IntNode next;
public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}
private IntNode first;
public SLList(int x) {
first = new IntNode(x, null);
}
/** Adds an item to the front of the list. */
public void addFirst(int x) {
first = new IntNode(x,first);
}
/** Retrieves the front item from the list. */
public int getFirst() {
return first.item;
}
}
addLast() and Size()
java
/** Adds an item to the end of the list. */
public void addLast(int x) {
IntNode p = first;
/* Advance p to the end of the list. */
while (p.next != null) {
p = p.next;
}
p.next = new IntNode(x, null);
}
java
/** Returns the size of the list starting at IntNode p. */
private static int size(IntNode p) {
if (p.next == null) {
return 1;
}
return 1 + size(p.next);
}
优化低效的Size()方法
在改变链表的Size时直接记录下,就可以不需要在Size()方法中遍历链表了
java
public class SLList {
... /* IntNode declaration omitted. */
private IntNode first;
private int size;
public SLList(int x) {
first = new IntNode(x, null);
size = 1;
}
public void addFirst(int x) {
first = new IntNode(x, first);
size += 1;
}
public int size() {
return size;
}
...
}
空链表( The Empty List)
创建空链表很简单(对于SLList),但会导致addLast()方法报错
java
public SLList() {
first = null;
size = 0;
}
java
public void addLast(int x) {
size += 1;
IntNode p = first;
while (p.next != null) {
p = p.next;
}
p.next = new IntNode(x, null);
}
p指向null,故p.next会出现空指针异常
addLast()改进(使用分支)
java
public void addLast(int x) {
size += 1;
if (first == null) {
first = new IntNode(x, null);
return;
}
IntNode p = first;
while (p.next != null) {
p = p.next;
}
p.next = new IntNode(x, null);
}
addLast()改进(更robust的做法)
对于存在很多特殊情况需要讨论 的数据结构,上面的方法就显得十分低效。
故我们需要考虑将其改进为一个具有普适性 的方法:将first改为sentinal.next
sentinal将会永远存在于链表的上位
java
package lists.sslist;
public class SLList {
public class IntNode {
public int item;
public IntNode next;
public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}
private IntNode sentinel;
private int size;
public SLList() {
sentinel = new IntNode(63,null);
size = 0;
}
public SLList(int x) {
sentinel = new IntNode(63,null);
sentinel.next = new IntNode(x, null);
size = 1;
}
/** Adds an item to the front of the list. */
public void addFirst(int x) {
sentinel.next = new IntNode(x, sentinel.next);
size += 1;
}
/** Retrieves the front item from the list. */
public int getFirst() {
return sentinel.next.item;
}
/** Returns the number of items in the list. */
public int size() {
return size;
}
/** Adds an item to the end of the list. */
public void addLast(int x) {
IntNode p = sentinel;
/* Advance p to the end of the list. */
while (p.next != null) {
p = p.next;
}
p.next = new IntNode(x, null);
}
/** Crashes when you call addLast on an empty SLList. Fix it. */
public static void main(String[] args) {
SLList x = new SLList();
x.addLast(5);
}
}