『算法』读书笔记 1.2数据抽象
Algorithms Fourth Edition
Chapter 1
本章结构
1.1 Java语法
1.2数据抽象
1.3集合类抽象数据类型:背包 (Bags) 、队列 (Queues) 、栈 (Stacks)
1.4面向操作的抽象数据类型
1.5连通性问题-Case Study: Union - Find ADT
1.2 Data Abstraction
ADT 是一种能够对使用者隐藏数据表示的数据类型。用Java类来实现抽象数据类型和用一组静态方法实现一个函数库没有什么不同。
Using Abstract Data Types
ADT和静态方法库的不同之处:
· 实例方法不需要static关键字;
· ADT中可能有构造函数constructor(和类名下共同但是没有返回值的函数);
· 用例代码:
1 .declare variables;
2.create objects to hold data-type values;
3.provide access to the values for instance methods to operate on them.
对象:能够承载数据类型的值的实体。
对象的三大特性:
数据类型的实现的唯一职责就是维护一个对象的身份,这样用例代码在使用数据类型时只需遵守描述对象行为的API即可,而无需关注对象状态的表示方法。
The implementation has the sole responsibility for maintaining an object;s identity so that the client code can use a data-type without regard to representation of its state by comforming to an API that describes an object's behavior.
创建对象 = 实例化;构造函数没有返回值,因为它总是返回它的数据类型的对象的引用。
静态方法的作用主要是实现函数;非静态(实例)方法的主要作用是实现数据类型的操作。区分:静态方法调用的开头是类名(习惯为大写),非静态方法调用的开头总是对象名(习惯为小写)。
赋值语句不会创建新的对象,而只是创建另一个指向某个已经存在的对象的引用--Aliasing-别名。
所有非原始数据类型的值都是对象-Arrays are objects.
Arrays of objects. An array of object is an array of reference to objects, not the objects themselves.
创建一个对象的数组需要:
1.调用数组的构造函数创建数组;
2.对于每个数组元素,调用它的构造函数创建相应的对象。 //重要!必须初始化每个元素
Counter[] rolls = new Counter[SIDES+1]; for (int i = 1; i <= SIDES; i++) rolls[i] = new Counter(i + "'s");区别于
int[] a = new int[N]; for (int i = 1; i < N; i++) a[i] = i * N;
Examples of Abstract Data Types
几何对象
· Interval2D-平面上的二维间隔,即和数轴对齐的长方形axis-aligned rectangles
· 计算几何图形面积和体积的问题可以转化为判定一个点是否落在该图形内
字符串
· A string is an indexed sequence of char values.
字符串和字符数组的区别:
· 数组通过内置语法访问每个字符,String则为索引访问、字符串长度以及其他许多方法准备了实例方法;
Implementing an Abstract Data Types
详解数据类型的定义类
实例变量
要定义数据类型的值(即每个对象的状态),需要声明实例变量。与局部变量相比,每时每刻每个local variables只有一次个值,而每个instance variables对应着无数值。
每个实例变量的声明都需要一个可见性修饰符visibility modifier。如果该值在初始化之后不应该再被改变,我们会使用final关键字。
构造函数
构造函数没有指定返回值的类型,构造函数名称和类名相同。
每个类至少含有一个构造函数以创建一个对象的标识。如果没有定义构造,类会隐式定义一个默认情况下不接受任何参数的构造函数并将所有实例变量初始化为默认值。
构造函数的作用是初始化实例变量。
当使用关键字new时,Java会自动触发一个构造函数。
实例方法
实例方法可以访问并操作实例变量,静态方法不能(静态方法没有实例变量)。
测试用例
为了强调用例和实现的分离,一般会将用例独立称为含有一个静态方法main()的类,并将数据类型定义中的main()方法预留为一个用于开发和最小单元测试的测试用例。
API、用例与实现
1.考虑client的需求
2.specify API-先确定用例代码,再确保API能够被实现
3.用Java类实现API的定义
4.设计client测试API是否满足需求
[Code]类Counter及其一个用例
public class Counter { private final String name; private int count; public Counter(String id) { name = id; } public void increment() { count++; } public int tally() { return count; } public String toString() { return count + " " + name; } } public class Flips { public static void main(Sting[] args) { int T = Interger.parseInt(args[0]); Counter heads = new Counter("heads"); Counter tails = new Counter("tails"); for (int t = 0; t < T; t++) if (StdRandom.Bernoulli(0.5)) //This statement is important heads.increment(); else tails.increment(); Stdout.printIn(heads); Stdout.printIn(tails); int d = heads.tally() - tails.tally(); Stdout.printIn("delta: " + Math.abs(b)); } }
四种变量的作用域scope:
参数变量Parameter variables-整个方法
局部变量Local variables-当前代码段中它的定义之后的所有语句
实例变量Instance variables-整个类
静态变量Static variables-和实例变量类似,但静态变量不和任何对象联系在一起,所以在一些语言中被称为“全局变量”
更多数据类型的实现
维护多个实现-Maintaining multiple implementation
可能会产生维护和命名问题-通过前缀的描述行修饰符区别同一份API的不同实现,同时维护一个没有前缀的参考实现,使之是用于大多数用例的需求
Data-type Design
继承
interface inheritance-子类型subtyping
implementation inheritance-子类subclassing-子类可以重新定义或重写父类的方法-extensible library
每个类都是Java的Object类的子类,因此每个类都含有getClass()可以查看两个对象是否出自于同一个类,是的话有相同的引用;toString();equals();hashCode()等方法。通常我们会重载这些方法以实现所需的行为。
脆弱的基类问题fragile base class problem
封装类型Wrapper Type
Java提供了一些内置的引用类型,称为封装类型。每种原始数据类型都有一个对应的封装类型:Boolean、Byte、Character、Double、Float、Integer、Long和Short分别对应着boolean、byte、character、double、float、integer、long和short。这些类主要由类似与parseInt()这样的静态方法组成。
等价性Equality
如果我们用相同类型的两个引用变量a和b进行等价性测试(a == b),检测的是它们的标识identity是否相同,即引用reference是否相同。Java规定equals()必须是equivalence relation.
它具有:自反性、传递行、对称性、一致性、非空性
[Code]判断对象等价性
//class Date implementation public class Date { private final int month; private final int day; private final int year; public Date(int m, int d, int y) { month = m; day = d; year = y; } public int month() { return month; } public int day() { return day; } public int year() { return year;} public boolean equals(object x)//1.先判断参数是否为空;2.再判断对象是否来自于同一个类,即getClass()返回相同的引用;3.转换对象,判断对象的具体实例变量是否相同 { if (this == x) return true; if (x == null) return false; if (this.getClass() != x.getClass()) return false; Date that = (Date)x; //supposed this conversion is definitely correct. if (that.month != this.month) return false; if (that.day != this.day) return false; if (that.year != this.year) return false; return ture; } }
内存管理
孤儿:
1.引用被赋值语句覆盖
Date a = new Date(12, 31, 1999); Date b = new Date(1, 1, 2001); a = b;2.对象离开作用域
不可变性Immutability
不可变数据类型Immutable Type:指该类型的对象的值在创建之后就无法被改变。eg:Date, String.不可变的抽象数据类型实例变量均为private final.
Java语言通过final修饰符来强制保证不可变性。不可变的数据类型比可变的数据类型使用更容易,误用更困难。但是对于引用类型的变量,final只能保证引用不被修改,至于对象的值就无能为力了。
可变数据类型Mutable Type:能够操作并改变对象的值。eg:Java数组, Counter.
契约式设计 Design by Contract
异常Exception:一般用于处理不受我们控制的不可遇见的错误-抛出异常或抛出错误
断言Assertion:用于验证我们在代码设计中作出的假设
An assertion is a boolean expression that you are affirming is true at that point in the program.
断言的作用在于调试。使用断言来保证代码永远不会被系统错误终止或者进入死循环。数据类型的设计者需要说明前提条件,后值条件和副作用。
【其他】
1.区别原始数据类型和引用类型的原因是性能。原始数据类型更接近计算机所支持的数据类型,因此使用他们的程序比引用类型的程序运行得更快。
2.弃用的方法deprecated method:不再被支持但为了保持兼容性而留在API的方法。