[end] CS61B Java06|Proj1: LinkedListDeque

创建自己的数据结构,实现双端队列deque(Double Ended Queue, 发音“deck”)。支持双端插入和移除的线性集合,支持容量限制的活没有固定大小的Deque。

A:linked nodes

通过链式节点实现Deque。

Creating the File

1 main函数中newLinkdListDeque中<>下有红色波浪线

Deque<Integer> lld = new LinkedListDeque<>();

解决,在类中声明

public class LinkedListDeque<T>;

2 main函数中new的这一行都被标记,按快捷键 Option+Enter(Mac). 选择“Make `LinkedListDeque’ implement Deque’并确认,会看到一系列方法,都需要填充进去
3 假设LinkedListDeque总是表示整数的Deque有点问题

public class LinkedListDeque<T> implements Deque<Integer>

解决:右击Integer,以此点击Refactor重构>Type Migraction类型迁移>将Java.lang.Integer替换为T>Refactor,然后所有错误的Integer都会被T替换
5 创建一个空的结构函数

Writing and Verifying the Constructor

在构造函数中写关于空列表的代码,这需要创建一个Node类,并引入一个或多个实例变量,构造函数时没有参数的。
节点nodes是双链表的,并具有双链表所需的字段,另外,只有一个node类,且node类需是哪部的,或LinkedLiseDeque的嵌套类。
问题:嵌套类是嵌套在类中,构造函数之外?还是也可以嵌套在构造函数内?–嵌套类及可以放置构造函数之外,也可以放在构造函数之内。
问题:构造函数是否需要写在类中的第一个,嵌套类可紧随其后?–都可以。
创建空列表时,自定义类型节点中的类型T可用null做初始值

addFirst and addLast

做添加操作时无论deque大小时间恒定,意味着操作不能使用遍历循环deque中的大量元素。
修改表链须注意顺序,前面步骤做的操作不能改变后面代码所需使用的内容。

Truth Assertions

Google’s Truth assertions library
@Test tells Java that this is method is a test, and should be run when we run tests. 告诉Java这个方法是一个测试,尽在运行测试时执行

assertThat(actual).isEqualTo(expected);
assertWithMessage("actual is not expected").that(actual).isEqualTo(expected); // add a message to the assertion
assertThat(actualList).containsExactly(0, 1, 2, 3).inOrder();
assertThat(ReferenceObject).containsExactlyElementsIn(expected).inOrder(); // `expected`is a object

isNull and isNotNull
isTrue and isFalse for booleans

getRecursive

getRecurive(index)递归创建获取值的方法,特殊用例getRecursive(0)=sentinel.next.next
getRecurive(index) 和 getRecurive(index - 1)不同索引对应的值之间有什么联系?没有联系
通过改变索引,现在索引i-1对应的值对应原有的索引i对应的item值,使得不同索引获得的对应值相同。
由于get方法是从sentinel下一个节点由0开始计数,所以只要sentinel指向下一个节点,移动后i-1位的item值即移动前i位的值。
getRecurive(index)中只有一个索引参数,如何传递引用元素节点,新建一个private递归方法,原有方法中返回这个private方法!

public T getRecursive(int index) {
    return getRecursive(index, sentinel);
}

private T getRecursive(int index, Node node) { //private访问控制 仅对同一类可见
    if(node.next == null) {
        return null;
    }
    else {
        return getRecursive(index - 1, node.next);
    }

removeFirst and removeLast

当Deque为空时,remove应返回null。remove相当于add方法逆向编码

B:backing array

通过回退阵列实现Deque

Constucor

回顾Array

// Array creation
x = new int[3]; //填充int默认值0 (String[3]内存盒中默认值null)
y = new int[]{1, 2, 3, 4, 5};
int[] z = {9, 10, 11, 12, 13}; // 结合变量声明盒创建z,省略new

回顾AList

Glorp[] items = (Glorp[]) new Object[8];
public class AList<Item> {
    private Item[] = items;
    private int size;
    /** Creates an empty list. */
    public AList() {
		items = (Item[]) new Object[100]; //java创建泛型的语法
		size = 0;
    }
    /** Resizes the underlying array to the target size. */
    private void resize(int capacity) {
    	Item[] a =Item[]) new Object[capacity];
    	System.arraycopy(items, 0, a, 0, size);
    	items = a;
    }
...

回顾LinkedListDeque

public class LinkedListDeque<T> implements Deque<T> {
    public class Node { // 构造嵌套类表示节点
        public Node prev;
        public T item;
        public Node next;

        public Node(Node m, T i, Node n){
            prev = m;
            item = i;
            next = n;
        }
    }
    /* The first item (if it exists) is at sentinel.next. */
    private Node sentinel;
    private int size;

    /* Creates an empty LinkedListDeque. */
    public LinkedListDeque() {
//        sentinel = new Node(sentinel, null, sentinel);
        sentinel = new Node(null, null, null);
        size = 0;
    }

    public static void main(String[] args) {
        Deque<Integer> lld = new LinkedListDeque<>();
//        lld.addFirst(10);
        lld.addLast(100);
    }
...

array实现循环

Resizing Up

不推荐在循环实现中使用arraycopy
思考如何实现的get方法和如何使用loop。
是否要新建一个int参数,以区分deque和array的大小

C:Deque Enhancements

Objects Methods

iterator()

Deque<String> lld1 = new LinkedListDeque<>();

  lld1.addLast("front");
  lld1.addLast("middle");
  lld1.addLast("back");
  for (String s : lld1.toList()) {
      System.out.println(s);
  }
  assertThat(lld1.toList()).containsExactly("front", "middle", "back");

注:以上相比官网增加了toList()

Deque接口的一个缺点是它不支持被遍历,使用循环或Truth库断言测试时会变异报错“foreach not applicable to type”、“Cannot resolve method containsExactly in Subject”。
为了解决这个问题,我们要将Deque接口扩展继承Iterable接口。

public interface Deque<T> extends Iterable<T> {

Lecture12:如何实现iterator()方法

Set<String> s = new HashSet<>();
...
for (String city : s) {
    ...
}
// 等价于
public Iterator<E> iterator();

Set<String> s = new HashSet<>();
...
Iterator<String> seer = s.iterator();
while (seer.hasNext()) {
    String city = seer.next();
    ...
}

完整如下:
思考:1 调用接口的迭代器方法返回迭代器对象,以实现迭代循环遍历功能;2 为类创建一个迭代器方法,该方法支持循环遍历功能;3 设计继承关系时,类须正式声明继承支持功能

import java.util.Iterator;

public class ArraySet<T> implements Iterable<T> { // 3 声明类支持循环
    private T[] items;
    private int size; // the next item to be added will be at position size

    public ArraySet() {
        items = (T[]) new Object[100];
        size = 0;
    }

    /* Returns true if this map contains a mapping for the specified key.
     */
    public boolean contains(T x) {
        for (int i = 0; i < size; i += 1) {
            if (items[i].equals(x)) {
                return true;
            }
        }
        return false;
    }

    /* Associates the specified value with the specified key in this map.
       Throws an IllegalArgumentException if the key is null. */
    public void add(T x) {
        if (x == null) {
            throw new IllegalArgumentException("can't add null");
        }
        if (contains(x)) {
            return;
        }
        items[size] = x;
        size += 1;
    }

    /* Returns the number of key-value mappings in this map. */
    public int size() {
        return size;
    }

    /** returns an iterator (a.k.a. seer) into ME */
    public Iterator<T> iterator() {
        return new ArraySetIterator();
    } //2.1 在类中创建一个返回迭代器的迭代器方法

    private class ArraySetIterator implements Iterator<T> { //2.2 嵌套私类实现迭代的循环遍历方法
        private int wizPos;

        public ArraySetIterator() {
            wizPos = 0;
        }

        public boolean hasNext() {
            return wizPos < size;
        }

        public T next() {
            T returnItem = items[wizPos];
            wizPos += 1;
            return returnItem;
        }
    }

    public static void main(String[] args) {
        ArraySet<Integer> aset = new ArraySet<>();
        aset.add(5);
        aset.add(23);
        aset.add(42);

        //iteration-简化
        //for (int i : aset) {
        //    System.out.println(i);
        //}
        //iteration-等价
        Iterator<Integer> seer = javaset.iterator(); //1.1 调用接口的迭代器方法创建一个迭代器对象,编译检查Set是否具备iterator()方法
        while (seer.hasNext()) {
            System.out.println(seer.next());
        } // 1.2 通过迭代器循环遍历,编译检查Iterator接口是否具备next/hashNext()方法
    }
}

即:1 在类中添加返回Iterator迭代器的iterator()方法; 2 迭代器Iterator应具有next/hasNext()方法; 3 在类定义行添加声明实现Iterable

equals()

    public boolean equals(Object obj) {
        return (this == obj);
    }

内容相同,但引用不同
提示:non-equal instance of same class with same string representation
instanceof 检查对象是否为某类的实例,包含了该对象为null的情况也可以正确处理

public boolean equals(Obiect o) {
    if (o == null) {false}
    return this.size == uddaDog.size;
}
//等价于
public boolean equals(Object o) {
    if (o instanceof Dog uddaDog) { // 1 检查o的动态类型是否为Dog; 2 隐式地将作为Dog的o转变味名为uddaDog的变量
        return this.size == uddaDog.size;
    }
    return false;
}

toString()

@Override
public String toString() {
   Strng returnString = "{";
   for (int i = 0; i < size - 1;  i += 1) {
       returnString += items[i].toString;
       returnString += ", ";
   }
   returnString += items[size - 1].toString;
   returnString += "}";
   return returnString;
}
// 以上很慢,即使向字符串添加一个字符也会生成一个全新的字符串(不可变类,速度和简单性的平衡),改写成使用字符串构建器
public String toString() {
   StrngBuilder stringSB = new StringBuilder("{");
   for (int i = 0; i < size - 1;  i += 1) {
       returnSB.append(items[i].toString);
       returnSB.append(", ");
   }
   returnSB.append(items[size - 1]);
   returnSB.append("}");
   return returnSB;
}

Guitar Hero

项目使用自制的ArrayDeque时,也在修正ArrayDeque。
若遇到Bug无法清晰定位时,可以通过日志打印(如打印循环中的i)来定位。
运行GuitarHeroLite,提示“TSM AdjustCapsLockLEDForKeyTransitionHandling - _ISSetPhysicalKeyboardCapsLockLED Inhibit“,意为键入大写C即可模拟划拨吉他dou弦发音。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值