List(CS61B学习记录)

文章探讨了Java中值传递与引用类型的差异,通过实例展示了如何在函数调用中处理基本类型和引用类型变量的变化。同时介绍了如何使用递归计算链表元素数量和优化链表的Size方法,以及处理空链表时的错误处理策略。
摘要由CSDN通过智能技术生成
  • 问题引入
    在这里插入图片描述

上图中,赋给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

在这里插入图片描述
在这里插入图片描述

使用递归求链表中元素的个数

/** 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!

不使用递归求元素个数

/** 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个元素

在这里插入图片描述

SList

接下来我们对IntList进行改进,使链表看上去不那么naked
在这里插入图片描述
有了节点类,现在我们补充上链表类

public class SLList {
    public IntNode first;

    public SLList(int x) {
        first = new IntNode(x, null);
    }
}

对比SLList和IntList,我们发现,在使用SLList时无需强调null

IntList L1 = new IntList(5, null);
SLList L2  = new SLList(5);

addFirst() and getFirst()

  /** 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

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()

/** 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);
}
/** 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()方法中遍历链表了

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()方法报错

public SLList() {
    first = null;
    size = 0;
}
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()改进(使用分支)
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将会永远存在于链表的上位

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);
    }
}

AList

public class AList {
    private int[] items;    
    private int size;
 
    public AList() {
      items = new int[100];  size = 0;
   }
 
    private void resize(int capacity) {
      int[] a = new int[capacity];
      System.arraycopy(items, 0, a, 0, size);
      items = a;
   }
 
    public void addLast(int x) {
      if (size == items.length) {
      resize(size + 1);
      resize(size * RFACTOR); //在多次调用addlast时有更好的性能表现
     }
      items[size] = x;
      size += 1;
   }


    public int getLast() {
      return items[size - 1];
   }
 
    public int get(int i) {
      return items[i];
   }
 
    public int size() {
      return size;
   }  
} 

Generic AList

public class AList<Glorp> {
  private Glorp[] items;    
  private int size;
 
  public AList() {
    items = (Glorp []) new Object[8];  
    size = 0;
  }
 
  private void resize(int cap) {
    Glorp[] a = (Glorp []) new Object[cap];
    System.arraycopy(items, 0, 
                     a, 0, size);
    items = a;
  }

  public Glorp get(int i) {
    return items[i];
  }
...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值