Head First Java (第二版)学习记录 6 - 解决bug (ArrayList 介绍)
2016-12-22 00:00
555 查看
1. bug分析
SimpleDotCom 类里的
所以我们需要一种机制来判别之前是否已经被猜中。
-------------------------------------------------------------------------
2. 解决方案分析
2.1 方案一
使用第二个数组。每当玩家猜中某一格时,我们就把相对的那一格设定成 true ,之后每次猜中都要检查是否在之前就已经被猜过了。
这个方案似乎太费时间了。那意味着每次玩家猜中某一格时你都必须要改变第二个数组的状态,且在之前你还需要检查该数组来判别此格是否已经被命中(当然,这是一个可行的方案)。
-------------------------------------------------------------------------
2.2 方案二
我们可以只用到原来的数组,将任何被命中的格子改为 -1 。这样值需要维护与检查一个数组。
相对来说,方案二比方案一要简单,但也不是很有效率。就算有些格子已经被命中了,你还是得逐个搜寻数组中的那些格子。
-------------------------------------------------------------------------
2.3 方案三
在命中某个格子时就把它删除掉,因此格子会越来越少。但是数组无法改变大小,因此我们必须做出新的数组并拷贝旧数组的值。
如果数组可以缩小的话,方案三会是3个方案里最好的一个。但是数组并不能执行这样的操作。
-------------------------------------------------------------------------
3. ArrayList
3.1 简介
ArrayList 就是动态数组,用 MSDN 中的说法,就是 Array 的复杂版本,它提供了动态的增加和减少元素,实现了 ICollection 和 IList 接口,灵活的设置数组的大小等好处。
-------------------------------------------------------------------------
3.2 定义
List 接口的大小可变数组的实现,位于 API 文档的 java.util.ArrayList<E> 。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)[1]
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单
在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
-------------------------------------------------------------------------
3.3 常用的方法
boolean add(E e)
将指定的元素添加到此列表的尾部。
void add(int index, E element)
将指定的元素插入此列表中的指定位置。
boolean addAll(Collection<? extends E> c)
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
boolean addAll(int index, Collection<? extends E> c)
从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
void clear()
移除此列表中的所有元素。
Object clone()
返回此 ArrayList 实例的浅表副本。
boolean contains(Object o)
如果此列表中包含指定的元素,则返回 true。
void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
E get(int index)
返回此列表中指定位置上的元素。
int indexOf(Object o)
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
boolean isEmpty()
如果此列表中没有元素,则返回 true
int lastIndexOf(Object o)
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
E remove(int index)
移除此列表中指定位置上的元素。
boolean remove(Object o)
移除此列表中首次出现的指定元素(如果存在)。
protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
int size()
返回此列表中的元素数。
Object[] toArray()
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
<T> T[] toArray(T[] a)
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前大小。
-------------------------------------------------------------------------
4. 修改 DotCom 游戏的源码
4.1 SimpleDotCom 类
-------------------------------------------------------------------------
4.2 SimpleDotCom 的测试类
-------------------------------------------------------------------------
4.3 GameHelper 类
(该类没有修改任何一个地方)
-------------------------------------------------------------------------
4.4 SimpleDotComGame 类
-------------------------------------------------------------------------
4.5 执行结果
SimpleDotCom 类里的
for (int cell : locationCells) { if (intGuess == cell) {//这个循环里我们没有去除已经被猜中的格子,所以可以重复猜同一格 result = "hit"; numOfHits++; break; } }
所以我们需要一种机制来判别之前是否已经被猜中。
-------------------------------------------------------------------------
2. 解决方案分析
2.1 方案一
使用第二个数组。每当玩家猜中某一格时,我们就把相对的那一格设定成 true ,之后每次猜中都要检查是否在之前就已经被猜过了。
这个方案似乎太费时间了。那意味着每次玩家猜中某一格时你都必须要改变第二个数组的状态,且在之前你还需要检查该数组来判别此格是否已经被命中(当然,这是一个可行的方案)。
-------------------------------------------------------------------------
2.2 方案二
我们可以只用到原来的数组,将任何被命中的格子改为 -1 。这样值需要维护与检查一个数组。
相对来说,方案二比方案一要简单,但也不是很有效率。就算有些格子已经被命中了,你还是得逐个搜寻数组中的那些格子。
-------------------------------------------------------------------------
2.3 方案三
在命中某个格子时就把它删除掉,因此格子会越来越少。但是数组无法改变大小,因此我们必须做出新的数组并拷贝旧数组的值。
如果数组可以缩小的话,方案三会是3个方案里最好的一个。但是数组并不能执行这样的操作。
-------------------------------------------------------------------------
3. ArrayList
3.1 简介
ArrayList 就是动态数组,用 MSDN 中的说法,就是 Array 的复杂版本,它提供了动态的增加和减少元素,实现了 ICollection 和 IList 接口,灵活的设置数组的大小等好处。
-------------------------------------------------------------------------
3.2 定义
List 接口的大小可变数组的实现,位于 API 文档的 java.util.ArrayList<E> 。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)[1]
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单
在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
-------------------------------------------------------------------------
3.3 常用的方法
boolean add(E e)
将指定的元素添加到此列表的尾部。
void add(int index, E element)
将指定的元素插入此列表中的指定位置。
boolean addAll(Collection<? extends E> c)
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
boolean addAll(int index, Collection<? extends E> c)
从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
void clear()
移除此列表中的所有元素。
Object clone()
返回此 ArrayList 实例的浅表副本。
boolean contains(Object o)
如果此列表中包含指定的元素,则返回 true。
void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
E get(int index)
返回此列表中指定位置上的元素。
int indexOf(Object o)
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
boolean isEmpty()
如果此列表中没有元素,则返回 true
int lastIndexOf(Object o)
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
E remove(int index)
移除此列表中指定位置上的元素。
boolean remove(Object o)
移除此列表中首次出现的指定元素(如果存在)。
protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
int size()
返回此列表中的元素数。
Object[] toArray()
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
<T> T[] toArray(T[] a)
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前大小。
-------------------------------------------------------------------------
4. 修改 DotCom 游戏的源码
4.1 SimpleDotCom 类
import java.util.ArrayList; public class SimpleDotCom { //private int[] locationCells;//修改为下行 private ArrayList<String> locationCells; //private int numOfHits;//修改上面以后就不需要此行了 /* public String checkYourself(String userInput){ int intGuess = Integer.parseInt(userInput); String result = "miss"; for (int cell : locationCells) { if (intGuess == cell) { result = "hit"; numOfHits++; break; } } if (numOfHits == locationCells.length) { result = "kill"; } System.out.println(result); return result; } *///此方法需要重写,如下 public String checkYourself(String userInput){ String result = "miss"; int index = locationCells.indexOf(userInput); if (index >= 0) { locationCells.remove(index); if (locationCells.isEmpty()) { result = "kill"; } else { result = "hit"; } } System.out.println(result); return result; } public void setLocationCells(ArrayList<String> loc){ locationCells = loc; } }
-------------------------------------------------------------------------
4.2 SimpleDotCom 的测试类
import java.util.ArrayList; public class SimpleDotComTestClass { public static void main(String[] args) { // TODO Auto-generated method stub SimpleDotCom dot = new SimpleDotCom(); /* int[] locations = {2,3,4}; dot.setLocationCells(locations); *///都不再使用数组,修改为下面的 ArrayList ArrayList<String> locations =new ArrayList<String>(); locations.add(Integer.toString(2)); locations.add(Integer.toString(2 + 1)); locations.add(Integer.toString(2 + 2)); dot.setLocationCells(locations); String userGuess = "2"; String result = dot.checkYourself(userGuess); String testResult = "failed"; if(result.equals("hit")){ testResult = "passed"; } System.out.println(testResult); } }
-------------------------------------------------------------------------
4.3 GameHelper 类
(该类没有修改任何一个地方)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class GameHelper { public String getUserInput(String prompt){ String inputLine = null; System.out.println(prompt + " "); try { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); inputLine = br.readLine(); if (inputLine.length() == 0) { return null; } } catch (IOException e) { // TODO: handle exception System.out.println("IOException: " + e); } return inputLine; } }
-------------------------------------------------------------------------
4.4 SimpleDotComGame 类
import java.util.ArrayList; public class SimpleDotComGame { public static void main(String[] args) { // TODO Auto-generated method stub int numOfGuess = 0; GameHelper helper = new GameHelper(); SimpleDotCom theDotCom = new SimpleDotCom(); int randomNum = (int)(Math.random()*5); /* int[] locations = {randomNum,randomNum + 1,randomNum +2}; theDotCom.setLocationCells(locations); *///改成下面的 ArrayList ArrayList<String> locations =new ArrayList<String>(); locations.add(Integer.toString(randomNum)); locations.add(Integer.toString(randomNum + 1)); locations.add(Integer.toString(randomNum + 2)); theDotCom.setLocationCells(locations); boolean isAlive = true; while (isAlive == true) { String guess = helper.getUserInput("enter a number:"); String result = theDotCom.checkYourself(guess); numOfGuess++; if (result.equals("kill")) { isAlive = false; System.out.println("You took " + numOfGuess + " guesses."); }//close if }//close while }//close main }
-------------------------------------------------------------------------
4.5 执行结果
相关文章推荐
- Head First Java (第二版)学习记录 5 - 编写程序 DotCom 初级游戏
- Head First Java (第二版)学习记录 3 - primitive主数据类型和引用
- Head First Java (第二版)学习记录 2 - 对象
- Head First Java (第二版)学习记录 4 - 方法操作实例变量
- 2009-03-07读书记录:《head first java》(第二版)——1至4章
- 记录-Head first java-1到3章
- java基础四 [构造器和垃圾回收](阅读Head First Java记录)
- <Head First Java>学习笔记--第三章:primitive主数据类型和引用
- headfirstjava 学习笔记(chapter1-5)
- 记录-Head first java-第五章
- java基础三 [深入多态,接口和多态](阅读Head First Java记录)
- head first series 学习记录(Python)
- HeadFirstJava学习心得——javaGUI编程
- java基础六 [异常处理](阅读Head First Java记录)
- java基础九[网络与线程](阅读Head First Java记录)
- java基础二(阅读Head First Java记录)
- <Head First Java>学习笔记--第一章:基本概念
- <Head First Java>学习笔记--第四章:方法操作实例变量 对象的行为
- DataNode上执行文件读写时报java.io.IOException: Bad connect ack with firstBadLink as 192.168.X.X错误解决记录
- java基础七 [图形用户接口](阅读Head First Java记录)