油管上的CS61B的视频
学习代码

随看随记

  • Java 值传递 赋值
  • 对于递归来讲,最重要的就是 base case(基础)和 neighbor(邻居)
  • 对于List的递归 尽量去判断其列表本身 list 多去迭代几次而不是 其list.rest
  • 对于List的迭代 尽量用迭代器 并不适用this 关键字
  • 静态方法只能改变静态变量,因为要遵守黄金法则。
  • java.util.Arrays.equals 可以比较两个数组内所有的项的内容是否相等。
  • 对于一个SLList来讲,SLList的地址是sentinel的地址,通过sentinel来对整个包装列表进行操作。

Hello World

  • Java中所有的代码都必须写在Class中
  • .java -> 编译(javac)->.class->解释器 -> 运行代码

Defining and Using Classes

  • 当人为编译 DogLauncher时 java会自动编译Dog 因为DogLauncher需要有Dog依赖才能继续运行
  • 仅仅声明 并未实例化
  • 仅仅实例化 但是没有对象赋值 无法使用 会直接被java当作垃圾回收
  • 将一个已经实例化以后的狗分配给已经声明的变量

References, Recursion, and Lists

  • 205.75 被储存到32位中 前16位是小数点前 后16位是小数点后


  • 声明 和 new 的创建对象

Java List 链表表示

原始List创建

  • IntList内存可视化
  • 正向创建链表 从开头开始 正向建表 到结束。

带有构造函数的List创建

  • IntList内存可视化
  • 反向列表 从结束开始创立 从写法上 感觉这种更加简单

获取List的长度l.size()(递归方法)

-

  • size方法递归可视化
  • 通过多次递归 寻找IntList中的null 在返回时 依次叠加

获取List的长度l.size()(迭代方法)

  • size方法迭代可视化

  • 在 iterativeSize中 p 可以直接通过l的指针this来得到 我好久没遇见这种写法了

总结练习

  • 在效率等方面 迭代更好 但是递归的代码更简洁

-get的递归写法内存图解

  • 注意学习一下递归的写法,要注意base case(基本盘)和neighbor(邻居 ),将beighbor想 base case上靠即可。

习题

  • 两个函数的可视化
  • 主要是迭代在List中的利用
  • 迭代要针对当前list 而不是而不是list.rest
  • List的迭代不要使用this 关键字

SLList 列表的包装类

  • 设计LIst包装类的目的就是 隐藏掉对象 只通过索引就可以找到相应的存储内容。

addFirst() 和 getFirst()

  • 其很重要的一点就是利用 addFirst 和 getFirst 来进行列表的填充和获取列表的首个的内容
  • 上面两个方法的可视化

安全与密封

  • 通过将IntNode 嵌入到SLList中来进行包装 并将其设置成私有和静态 来拒绝外部的直接访问,提高了SLList的安全性
public class MySLList {// 在外界不需要关注并且需要保护的情况下 需要将其设置成私有// 在外界不需要操作 嵌套类的时候 可以将其设置成 静态 static// 将static 看成 never looks outwards private static class MyIntNode {public int item;public MyIntNode next;public MyIntNode(int i, MyIntNode n) {item = i;next = n;}}private MyIntNode first;public MySLList(int x) {first = new MyIntNode(x, null);}/*** 返回列表中的第一项*/public int getFirst(){return first.item;}/*** 添加一个int 到list的最前面*/public void addFirst(int x){first = new MyIntNode(x, first);}public static void main(String[] args) {MySLList L = new MySLList(10);L.addFirst(15);}
}

addLast() 和 size()

  • 两个方法的可视化 size的迭代的方法也在里面
  • 在包装后类如果想实现递归 就需要 建立一个私有的对外非公开的方法来代替原有的方法进行递归操作 用其输入参数的来进行递归操作
  • 个人感觉在包装以后 迭代操作会更加容易一点 但是递归需要建立私有静态类的思想要了解。

Cache

  • 使用递归来获得size的方法复杂度过高 因此引入size成员变量 来进行缓存 每次进行 add操作的时候仅仅需要操作size get的时候仅仅需要 return this.size 就可以了 大大减小了时间的消耗

Empty List

  • 主要解决的问题 在存在空构造之后 会有可能存在第一个节点的对象为null,需要额外用if语句进行条件判断。如下所示,因此引入哨兵节点。
  • 内存可视化
/*** @description:* @program: CS61B* @author: paleatta* @date: 2021-05-18 09:38* @version: jdk1.8**/
public class MySLList {// 在外界不需要关注并且需要保护的情况下 需要将其设置成私有// 在外界不需要操作 嵌套类的时候 可以将其设置成 静态 static// 将static 看成 never looks outwardsprivate static class MyIntNode {public int item;public MyIntNode next;public MyIntNode(int i, MyIntNode n) {item = i;next = n;}}private MyIntNode first;private int size;public MySLList() {first = null;size = 0;}public MySLList(int x) {first = new MyIntNode(x, null);size = 0;}/*** 添加一个int到list的最前面*/public void addFirst(int x) {first = new MyIntNode(x, first);size++;}/*** 迭代* 添加一个int到list的最后面*/public void addLast(int x) {if (first == null){first = new MyIntNode(x, null);return ;}MyIntNode p = first;while (p.next != null) {p = p.next;}p.next = new MyIntNode(x, null);size++;}/*** 通过缓存来减小时间上的消耗*/public int cacheSize() {return size;}public static void main(String[] args) {MySLList L1 = new MySLList();L1.addFirst(10);L1.addFirst(5);L1.addFirst(20);int size = L1.cacheSize();System.out.println(size);}
}

Sentinel Node

  • 对于SLList来说,用哨兵节点来当作第一个List的结点,在空构造中使用,将哨兵节点的next置为空而不是直接将first置为null,从而避免了空指针开始的问题。应该也是目前List第一个都是无用指针的之一吧。
  • 带有哨兵节点的可视化
/*** @description:* @program: CS61B* @author: paleatta* @date: 2021-05-18 09:38* @version: jdk1.8**/
public class MySLList {// 在外界不需要关注并且需要保护的情况下 需要将其设置成私有// 在外界不需要操作 嵌套类的时候 可以将其设置成 静态 static// 将static 看成 never looks outwardsprivate static class MyIntNode {public int item;public MyIntNode next;public MyIntNode(int i, MyIntNode n) {item = i;next = n;}}private MyIntNode sentinel;private int size;public MySLList() {//这里的-1 是随意的 可以随便取 因为用不到sentinel = new MyIntNode(-1, null);size = 0;}public MySLList(int x) {sentinel = new MyIntNode(x, null);size = 1;}/*** 返回列表中的第一项*/public int getFirst() {return sentinel.next.item;}/*** 添加一个int到list的最前面*/public void addFirst(int x) {sentinel.next = new MyIntNode(x, sentinel.next);size++;}/*** 迭代* 添加一个int到list的最后面*/public void addLast(int x) {MyIntNode p = sentinel.next;while (p.next != null) {p = p.next;}p.next = new MyIntNode(x, null);size++;}/*** 通过缓存来减小时间上的消耗*/public int cacheSize() {return size;}/*** 老师的版本* 主要是由与SLList本身不是MyIntNode类* 无法进入递归 因此由多写了一个私有的对外部不开放的函数* 用其参数的来进行递归*/public int size() {return size(sentinel.next);}private static int size(MyIntNode p) {if (p.next == null) return 1;return size(p.next) + 1;}/*** 递归* 返回SLList的长度*/public int MySize() {int size = 0;MyIntNode p = sentinel.next;while (p != null) {size++;p = p.next;}return size;}public static void main(String[] args) {//        MySLList L = new MySLList(15);
//        L.addFirst(10);
//        L.addFirst(5);
//        L.addFirst(20);MySLList L1 = new MySLList();L1.addFirst(10);L1.addFirst(5);L1.addFirst(20);int size = L1.size();System.out.println(size);}
}
  • 哨兵节点中要注意:
  1. 在原来SLList中是使用first作为链表的第一个节点,但是在这里的第一个节点不是sentinal,而是sentinal.next,哨兵节点本身不作为链表中的一员,仅仅作为开头,放置出现null而产生了,其item也毫无作用
  2. 只要生成以一个SLList 其sentinel的地址是不会改变的,因此可以将其设置为final

Invariants

DLList

introduce

  • 对于原来的单向列表来讲,在针对于最后一位,或者列表中的某一位进行添加、更新或者删除时需要迭代整个链表,比较复杂。在更新或添加最后节点虽然可以用添加Last缓存进行加速,但是对其他的操作依然无法优化。
    private final MyIntNode sentinel;private MyIntNode last;private int size;
  • 因此引入双向链表DLList

Doubly Linked Lists

  • 因为是双向链表,在最开始时候,list的的头尾的next可能是null,也可能是sentinel node ,为了解决这个问题:
    -
  • 在引入双向链表时要注意最后一个的节点的问题有以上两种方案
  • 和前面的哨兵节点一样,在链表的最后添加一个哨兵节点。
  • 将最后的哨兵节点的next和sentinel.next相连接,变成循环链表。

Generic (泛型)

  • 在以前我们建立的list仅仅能用于int类型,但是数据类型有很多,因此引入泛型来让其更加通用
public class MySLList<Generic> {// 在外界不需要关注并且需要保护的情况下 需要将其设置成私有// 在外界不需要操作 嵌套类的时候 可以将其设置成 静态 static// 将static 看成 never looks outwardsprivate class MyGenericNode {public Generic item;public MyGenericNode next;public MyGenericNode(Generic i, MyGenericNode n) {item = i;next = n;}}private final MyGenericNode sentinel;private MyGenericNode last;private int size;}

array Overview

  • 数组有长度 但是数组没有方法 创建时 有且仅有一个引用。
  • 2D Arrays

二维数组的内存显示

  • array vs class
  • 对于array来讲 其只是一段连续的存储空间,其寻找存储内容需要序号
  • 对于class来讲 在不使用反射的条件下 其是一段强编码后进行存储 其只能对于属性进行操作 无法对于索引进行操作
    他们都作为引用类型 其在栈中的存储类型都是32位的引用地址。
  • java vs other language
  • 与其他语言的数组相比,Java数组:
    对于“slicing”没有特殊的语法(例如在Python中)。
    无法缩小或扩展(例如在Ruby中)。
    没有成员方法(例如Javascript)。
    必须仅包含相同类型的值(与Python不同)。

AList

Why Use Array

  • 对于双向列表来说 很难实现用索引来寻找列表中相应的对象,其中一种的解决方法就是借助数组的结构进行实现。

Alist

  • 最基础的Alist版本
public class MyAList {private int[] items;private int size;public MyAList() {items = new int[100];size = 0;}//添加到末尾 扩容时默认扩容100public void addLast(int item) {items[size] = item;size++;}public int getLast() {return items[size - 1];}public int get(int index) {return items[index];}public int size() {return size;}
}
  • removeLast 删除嘴都一项
  • 在AList中是通过Size确定数组的长度的 虽然最后一个item没有修改 但是在使用时已经将其忽略掉 并且在下次使用addLast的时候会将其覆盖掉 因此可以互用管它 也可以像注释里一样 设计一个逻辑删除位 但是不是必要的。
    /*** removeLast()*/public int removeLast() {int x = items[size];//items[size - 1] = 0;size--;return x;}
  • Alist的扩容问题
  • 数组的长度在内存中本身是固定的 我们看起来长度在变化是将size作为AList的长度来看待。
  • 当数组的长度不足时 需要进行扩容补充。
  • 我看有一种代码风格是写两个重载函数,一个有参,一个缺参,缺少参数的是走默认配置,完全参数的是呗其他调用,类似这样,不知道这样是不是一个好习惯。
  • 添加一个数组是我自己加的 还没有进行测试。
    /*** 默认扩容100*/private void resize() {resize(size + 100);}/*** 调整容量到目标容量* @param capacity*/private void resize(int capacity) {int[] temp = new int[capacity];System.arraycopy(items, 0, temp, 0, size);items = temp;}//添加到末尾 扩容时默认扩容100public void addLast(int item) {if (size >= items.length) resize();items[size] = item;size++;}//添加数组public void addLast(int[] is) {if (size + is.length >= items.length) resize(size + is.length);//        items[size] = item;System.arraycopy(is, 0, items, size, is.length);size += is.length;}

resize的速率问题

  • 每次达到数组大小限制时,便开始调整数组大小。这非常慢,因为将数组复制到新数组意味着我们必须对每个项目执行复制操作。最糟糕是,由于我们仅调整了一个额外的框的大小,因此,如果我们选择添加另一项,则每次添加到数组时都必须再次执行此操作。

  • 相较于LinkedList来说 AList每次都需要扩容和复制,花费的时间更多,而LinkedList来说,更多的时间是操作指针,其复杂度不高,花费的时间也就更少。

  • 提高调整大小性能而不是添加一个额外的框,我们可以创建一个包含size * FACTOR项的新数组,其中FACTOR可以是任何数字,例如2。我们将在本课程的稍后部分讨论为什么这样做很快。

  • 原来的扩容是+size 是线性的 现在用* 是几何增长 大大减小了时间

    private void resize(int capacity) {int[] temp = new int[capacity];System.arraycopy(items, 0, temp, 0, size);items = temp;}

Generic 通用AList

  • 将智能存储整数型转化成能存储通用类型的数组。

主要注意以下几点

  • java不允许创建泛型数组对象 因此只能创建object[] 并将其类型强制转化成Generic[]
  • removeLast() 在只有int类型是我们认为可以不对超出的size的那一位进行操作,但是在Generic中,将超出的那一位置为null可以让GC回收其引用对象在堆中的垃圾,节约空间
public class MyAList<Generic> {private Generic[] items;private int size;public MyAList() {items = (Generic[]) new Object[100];size = 0;}/*** 默认扩容100*/private void resize() {resize(size * 2);}/*** 调整容量到目标容量** @param capacity*/private void resize(int capacity) {Generic[] temp = (Generic[]) new Object[capacity];System.arraycopy(items, 0, temp, 0, size);items = temp;}//添加到末尾 扩容时默认扩容100public void addLast(Generic item) {if (size >= items.length) resize();items[size] = item;size++;}//添加数组public void add(Generic[] is) {if (size + is.length >= items.length) resize(size + is.length);//        items[size] = item;System.arraycopy(is, 0, items, size, is.length);size += is.length;}public Generic getLast() {return items[size - 1];}public Generic get(int index) {return items[index];}public int size() {return size;}/*** removeLast()*/public Generic removeLast() {Generic returnItem = items[size];items[size] = null;size--;return returnItem;}
}

Testing

JUnit

  • 关于assert的小知识
 import static org.junit.Assert.*;//对两个数组进行断言判定 //如果两个数组相同 那么通过//不相同则返回异常assertArrayEquals(expected, input);
  • 其效果和下面是一样的

选择排序 Selection Sort

  • 选择排序的编写加测试的步骤如上
public class MySort {/*** 实现选择排序一共由三个步骤* 1 寻找最小的项* 2 把它移动到最前面* 3 重复进行1和2步骤 (使用递归?)* @param x*///对外排序的接口 其实际是调用 sort重载方法来实现的public static void sort(String[] x ){sort(x,0);}private static void sort(String[] x ,int start) {if (start == x.length) return;/*1. 寻找最小的项目*/int smallest = findSmallest(x , start);/*2. 把它移动到最前面*/swap(x,start,smallest);/*3. 递归排序剩下的部分*/sort(x,start+1);}/*1. 寻找最小的项目,从start开始*/private static int findSmallest(String[] x , int start) {int smallestIndex = start;for (int i = start; i < x.length; i++) {int flag = x[smallestIndex].compareTo(x[i]);if (flag > 0) smallestIndex = i;}return smallestIndex;}/*2. 交换指定的两个item*/private static void swap(String[] x, int a, int b) {String temp = x[a];x[a] = x[b];x[b] = temp;}
}

Inheritance, Implements

继承与重载

  • 两者各有用武之地
  • 对于继承来讲 在有上下级关系的时候比较好用 例如课中的AList 和SLList实现了(继承的一种)List中的方法,可以减少写重载函数的代码,看起来更加美观,逻辑上也更加舒服。
  • 重载是两个方法之间,根据签名不同进行区分的相同函数名的方法。重载主要强调参数不同,继承主要强调上下级关系。
  • 继承一般来说都是使用实现的子类或继承的子类。在子类中创建对象后通过调用方法,对父类的函数进行重写。
 //声明中虽然使用的是List 是父类 ,但是实际上是调用了子类的对象,因此用父类的声明,但是走的是子类的方法。// 这里可以结合java堆栈的知识相关先联想一下,就比较容易理解。List<String> someSLList = new SLList<>();List<String> someAList = new AList<>();

default Methods and override default method

  • 可以通过default实现接口的向下重写 这个意思表示 在AList对象,还是在SLList对象中都会调用接口的print方法
public interface List<Item> {default public void  print(){for (int i = 0; i < size(); i++) {System.out.print(get(i));}System.out.println();}
}public static void main(String[] args) {List<String> someSLList = new SLList<>();someSLList.addFirst("elk1");someSLList.addLast("dwell");someSLList.addLast("on");someSLList.addLast("existential");someSLList.addLast("crises");someSLList.print();List<String> someAList = new AList<>();someAList.addFirst("elk2");someAList.addLast("dwell2");someAList.addLast("on");someAList.addLast("existential");someAList.addLast("crises");someAList.print();}
  • 其 输出是这样的 (先不要关注elk2 没有 这是小问题)
  • 由于这么写对于SLList的print效率很低,因此需要在SLList对print方法进行重写。
 @Overridepublic void print() {System.out.println("THIS IS THE OVERRIDDEN VERSION.");Node p = sentinel.next;while (p != null) {System.out.print(p.item + " ");p = p.next;}}
    public static void main(String[] args) {List<String> someSLList = new SLList<>();someSLList.addFirst("elk1");someSLList.addLast("dwell");someSLList.addLast("on");someSLList.addLast("existential");someSLList.addLast("crises");someSLList.print();List<String> someAList = new AList<>();someAList.addFirst("elk2");someAList.addLast("dwell");someAList.addLast("on");someAList.addLast("existential");someAList.addLast("crises");someAList.print();}
  • 在AList中没有写print方法,因此 在AList中不会打印语句
  • 因此我认为在SLList中编写了print 方法后会,default关键字会失效 虽然在List中有其声明 但是 其已经不走父类的方法了 只看继承对象的方法了。

Dynamic Methods Selection and Overrided

  • 在java语言中,包括静态类型和动态类型。静态类型是生命是的类型,其声明以后是不可以改变的。动态类型是在new的时候创建的,是动态类型的,可以接收继承自他的类型。在后面进行调用的时候就是调用对象(动态)的方法。动态类型是runtime类型。
  • greet只在父类中存在。sniff在父类中有,但是由于子类也有sniff且签名相同,因此在Dog类中将其重写了。在flatter方法中,因为其签名不相同,因此没有将其重写,可以当作重载函数处理,父类的入参是Animal类型,Dog的入参是Dog类型。

Conclusion

  • 对于继承来说,其最重要的是实现类之间的关系,而不是某个类用有哪些方法。但是接口也可以方便去拓展功能。

Extends

 //构造函数也可以这么写private final SLList<Item> lostItems = new SLList<>();//=========================================//可以不写super 在编译的时候会自动使用父类的无参构造,写不写super();都一样。public VengefulSLList() {//        super();lostItems = new SLList<>();}

继承打破

  • 当进入bark方法时,会调用this.barkMany的方法,因为动态类型是子类,因此会走重写后的方法,因此会再次进入重写后的barkMany方法,因此会进入死循环。

保守编译与强制转换

  • 编译器会非常保守的仅使用声明的静态类型进行编译判定。因此会出现动态类型可以通过,但是由于静态类型中不存在此方法而编译失败。以及在类型转换的时候,父类的对象无法转换成声明的子类类型。
  • 强制转换cast,强制转换是程序员麻痹编译器的一种方式,有时强制转换是父类到子类,可以转换,但是有时是两个不相关的类,无法转换,这是很危险地。因此需要小心使用强制转换。

方法传递

  • Java中的方法传递通常使用调用对象方法去进行获得。

  • 通过实现接口的方式,对子类进行方法的调用,以及对象方法的调用。也就是我们常说的多态性。

Conlusion

编译器规则

Compiler allows the memory box to hold any subtype.
Compiler allows calls based on static type.
Overriden non-static methods are selected at runtime based on dynamic type.
For overloaded methods, the method is selected at compile time.

  • 在代码中不应该出现
  • 超类和子类有相同的变量名。
  • 超类和子类有相同的静态方法,且其前名相同。这是override是不生效的,因为override只对成员函数生效。

interface 的多态

  • 在这其中的本质是虽然其声明的静态类型有所变化,但是其动态类型(runtime type) 是没有变化的,一直是Dog类型。因此其虽然在代码的过程中其声明类型变化,但是其可以转换回Dog类型。
  • 其感觉更像是,Dog在实现了Comparable 这个接口以后,可以通过Comparable 的类型对Dog类的对象进行比较。

Comparable 的好处

  • 主要是为了解决类型的强制转换问题,以及无法转化的类型被要求强制转化而导致的error。
  • 可以通过增加泛型来减少强制转换

Comparator 比较器

  • Comparator 和 Comparable的区别,比较器是生成一个比较器对象,用来比较两个对象。comparable 是提供一个对象,将其和自己进行比较。
  • 实现了比较器以后,两个对象之间的比较只需要通过比较器即可。通过调用比较器的方法来实现比较。
  • 一般来说,比较器是私有的,大都仅通过类的静态方法来获取一个比较器。

public class MyDog implements Comparable<MyDog> {public String name;private int size;public MyDog(String name, int size) {this.name = name;this.size = size;}//    @Overridepublic int MyCompare(Object item) {MyDog dog = (MyDog) item;return size - dog.size;}@Overridepublic int compareTo(MyDog dog) {return size - dog.size;}private static class NameComparator implements Comparator<MyDog> {@Overridepublic int compare(MyDog o1, MyDog o2) {// 这里的compareTo不是上面的方法// 是实现了Comparator以后的自带的方法return o1.name.compareTo(o2.name);}}public static Comparator<MyDog> getNameComparator(){return new NameComparator();}
}
public class MyMaximizer {public static Comparable max(Comparable[] items){int maxDex = 0;for (int i = 0; i < items.length; i += 1) {int cmp = items[i].compareTo(items[maxDex]);if (cmp > 0) {maxDex = i;}}return items[maxDex];}
}
public class MyDogLauncher {public static void main(String[] args) {MyDog d1 = new MyDog("Elyse", 3);MyDog d2 = new MyDog("Sture", 9);MyDog d3 = new MyDog("Benjamin", 15);MyDog[] myDogs = new MyDog[]{d1, d2, d3};MyDog d = (MyDog) MyMaximizer.max(myDogs);System.out.println(d.name);Comparator<MyDog> nc = MyDog.getNameComparator();int flag = nc.compare(d1, d2);System.out.println(flag);if (flag > 0) {System.out.println(d1.name);return;}System.out.println(d2.name);}
}

Clonclusion

  • Comparator 和 Comparable之间没有任何关系,实现了Comparable便是可以实现比较,Comparator 表示可以实现两个对象之间的特殊比较。
  • 接口的功能相当于创造一个回调函数

List and Set in Java

Basic ArraySet

  • Github里面都有代码,我这里就不copy了。
  • 对于开头添加null来讲,将其作为异常抛出,或者将其作为一个对象存入,都是可行的策略 只要做好文档即可。

iterable 和 iterator

  • 对于一个类能实现增强for循环有两个必要条件
  • 该类实现了Itreable,并重写其相关方法,返回迭代器。
  • 迭代器中重写了next方法和hasnext方法,并使其有作用
  • 如果没有继承Iterable 但是完成了所有后面的步骤,则可以使用迭代器iterator进行比较难看的迭代操作。
        MyArraySet<Integer> aset = new MyArraySet<>();…………………………………………………………Iterator<Integer> iterator = aset.iterator();while (iterator.hasNext())System.out.println(iterator.next());
  • 继承了Iterable 就可以进行增强for循环了
        MyArraySet<Integer> aset = new MyArraySet<>();…………………………………………………………//iterationfor (int i : aset) {System.out.println(i);}
  • 如果你打过断点,你就会发现,起始增强for循环也是走的迭代器。在
        for (int i : aset) {//首先调用public Iterator<T> iterator() {} 获得迭代器//调用hasNext()方法 查看是否为true//如果为true调用iterator.next(),索引移动到i+1 返回第i个item//为false则跳出循环System.out.println(i);}
  • 其实iterable 和 iterator 与 Comparable 和Comparator有相似处。
  • 私自获得比较器或迭代器 , 通过实现able的方法 来进行和自身相关的操作。尤其是在书写代码方面,有很高的相似度。

toString

  • String StringBuilder StringBuffer 异同
  • String 内存浪费内存严重
  • StringBuffer 内存不浪费 线程安全 效率低
  • StringBuilder 内存不浪费 线程不安全 效率高
  • 通常情况下建议使用StringBuilder 要求线程安全的情况下使用StringBuffer 减少直接使用String 尽量从Stringbuilder 或者StringBuffer转换过去。

equals 和 ==

  • equals的默认写法就是 仅仅通过判断“==”返回布尔值。
public boolean equals(Object obj) {return (this == obj);
}
  • 注意T和this的类型并不相同,为什么还可以成功
  • 因为迭代器会进入迭代器的hasNext()方法,判定否继续执行。和本身的T 与 this的类型无关
 //equals的代码@Overridepublic boolean equals(Object other) {if (other == this) return true;if (other == null) return false;if (other.getClass() != this.getClass()) return false;MyArraySet<T> o = (MyArraySet<T>) other;if (o.size != this.size) return false;//注意这里T和this的类型并不相同,为什么还可以成功for (T item : this) {if (!o.contains(item)) return false;}return true;}

Do better about String

  • 查了一下,String.join是一次性申请好所有的内存,因此效率会比“+”高很多。
    @Overridepublic String toString() {List<String> stringItems = new ArrayList<>();for (T x : this) {stringItems.add(x.toString());}return "{"+String.join(",", stringItems)+"}";}

数据结构与算法 学习笔记(上)相关推荐

  1. 数据结构与算法学习笔记之 提高读取性能的链表(上)

    数据结构与算法学习笔记之 提高读取性能的链表(上) 前言 链表(Linked list)比数组稍微复杂一点,在我们生活中用到最常见的应该是缓存,它是一种提高数据读取性能的技术,常见的如cpu缓存,浏览 ...

  2. 数据结构与算法学习笔记之 从0编号的数组

    数据结构与算法学习笔记之 从0编号的数组 前言 数组看似简单,但掌握精髓的却没有多少:他既是编程语言中的数据类型,又是最基础的数据结构: 一个小问题: 为什么数据要从0开始编号,而不是 从1开始呢? ...

  3. 数据结构与算法学习笔记——链栈

    数据结构与算法学习笔记(C语言) 链栈 在开始链栈的学习之前,我们先实现一下上一篇文章中提到的检查括号匹配的小程序,鉴于水平有限,本人就随便写一下代码好了,目标仅限于对功能的实现. /*用顺序栈这种数 ...

  4. 数据结构与算法学习笔记4:递归+分治法

    数据结构与算法学习笔记4 递归 斐波那契数列 青蛙跳台阶问题 链表倒序打印 分治法 二分查找/折半查找 Binary Search 题目1:快速幂 题目2:如何判断一个数是否为2的次幂 递归 指在函数 ...

  5. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  6. 数据结构与算法学习笔记——图 C++实现

    数据结构与算法学习笔记--图 C++实现 1 概念 2 图的表示方法 3 算法 3.1 拓扑排序 3.2 图的搜索算法 3.2.1 广度优先搜索(BFS) 3.2.2 深度优先搜索(DFS) 3.3 ...

  7. 数据结构与算法学习笔记之先进先出的队列

    前言 队列是一种非常实用的数据结构,类似于生活中发排队,可应用于生活,开发中各个方面,比如共享打印机(先请求先打印),消息队列.你想知道他们是怎么工作的么.那就来一起学习一下队列吧 正文 一.队列的定 ...

  8. 数据结构与算法 学习笔记(5):字符串

    数据结构与算法 学习笔记(5)- 字符串 本次笔记记录了LeetCode中关于字符串的一些问题,并给出了相应的思路说明和代码.题目编号与LeetCode对应,方便查找. 题目1:LeetCode 13 ...

  9. 数据结构与算法 学习笔记(8):字典、集合、哈希表

    数据结构与算法 学习笔记(8):字典.集合.哈希表 本次文章记录的是和字典.集合.哈希表等数据结构相关的LeetCode算法题(题号与LeetCode对应),包括其构造和使用,针对每一题或一类题给出了 ...

  10. 【数据结构与算法学习笔记】

    文章目录 前言 0 Preview与算法复杂度分析简述 1 线性数据结构 1.1 Stack 1.2 Queue 1.3 Deque 1.4 UnorderedList 1.5 OrderedList ...

最新文章

  1. Excel VBA 教程
  2. BZOJ 3732 Network
  3. 搜索推荐炼丹笔记:酒店搜索位置偏差的边际重要性
  4. junit测试设置不回滚_正确设置JUnit测试名称
  5. Java集合(二):List列表
  6. VUE:组件间相互跳转、页面带参跳转
  7. 游戏开发中的数据表示
  8. Java LocalDate类| toString()方法与示例
  9. (147)FPGA面试题-Verilog移位相加实现乘法(二)
  10. react 注释html,React之JSX语法
  11. Linux 命令(54)—— trap 命令(builtin)
  12. 使用压缩包安装mysql
  13. matlab RFID解码,UHF RFID编码之TPP编码
  14. android 圆角 水波纹_Android实现水波纹点击效果
  15. ubuntu 清空回收站
  16. 我是如何从电脑小白走上编程之路
  17. ZN-IRF02工业机器人视觉分拣实训系统
  18. 很多人觉得做地推很没有面子
  19. 一度智信|拼多多店铺取名大全
  20. VPS和云服务器的区别

热门文章

  1. 数据可视化:绘制部分图表的核心代码实现
  2. 分数换算小数补0法_一年级数学0基础的全过来,最全知识点及基本方法,包你数学不补课都90+...
  3. C语言和C++的文件读取、写入函数
  4. PF_RING 6.0.2发布
  5. redis分布式锁实践 并实现看门狗锁续期机制
  6. 神经网络适用于分类问题的最后一层-Softmax和交叉熵损失介绍及梯度推导
  7. 【DP】poj1671
  8. Mysql 内存管理及优化
  9. Android KK平台的一个bug----在收到内容只有一个“=”的信息后,手机自动重启
  10. html自定义列表标签报错,HTML入门标签汇总