在平常生活中,人们常用列表:待办事项列表,购物清单,十佳榜单,最后十名榜单等。计算机也在使用列表,尤为是列表中元素保存的是太多时。当不须要一个很长的序列中查找元素,或对其进行排序时,列表显得尤其有用。反之,若是数据结构很是复杂,列表的做用就没有那么大了。javascript
本章展现了若是建立一个简单的列表类,咱们首先给列表给出抽象的数据类型定义,而后描述如何实现抽象数据类型(ADT),最后,分析几个列表适合解决的实际问题。html
一,列表的抽象数据类型定义java
为了设计列表的抽象数据类型,须要给出列表的定义,包括列表应该有哪些属性,应该在列表上执行哪些操做。数组
列表是一组有序的数据,每一个列表中的数据项称为元素。在javascript中,列表中的元素能够是任意数据类型。列表中能够保存多少元素并无限定(在实际使用时会受到程序内存的限制)。数据结构
不包含任何元素的列表称为空列表。列表中包含元素的个数称为列表的length。在内部的实现上,用一个变量的listSize保存列表中元素的个数。能够在列表的末尾append一个元素,也能够给列表起始位置insert一个元素。使用remove方法从列表中删除元素,使用clear方法清空列表中的全部的元素。app
还可使用toString()方法显示列表中全部的元素,使用getElement()方法显示当前元素。 列表中拥有描述元素位置的属性。列表有前有后(front和end),使用next()方法能够从当前元素移动到下一个元素,使用prev()方法能够移动当前元素到上一个元素。还可使用moveTo(n)方法直接移动到指定的位置。currPos属性表示列表中当前的位置。函数
列表的抽象数据类型并未指明列表的存储结构,在本章的实现中,咱们使用一个dataStore来存储元素。post
列表的抽象数据定义。测试
listSize(属性) | 列表中元素的个数 |
pos(属性) | 列表的当前位置 |
length(属性) | 返回列表中元素的个数 |
clear(方法) | 清空列表中全部的元素 |
toString(方法) | 返回列表中的字符串形式 |
getElement(方法) | 返回当前位置的元素 |
insert(方法 ) | 在现有元素后插入新元素 |
append(方法) | 在列表末尾增长新元素 |
remove(方法) | 从列表中删除元素 |
front(方法) | 将列表中的当前元素设置到第一个元素 |
end(方法) | 将列表中的当前元素位置移动到最后一个 |
prev(方法) | 将当前位置后移一位 |
next(方法) | 将当前位置前移一位 |
currPos(方法) | 返回列表的当前位置 |
moveTo(方法) | 将当前位置移动到指定位置 |
二,实现列表类this
根据上面定义的列表抽象数据类型,能够直接实现一个List类,让咱们从定义构造函数开始,虽然它自己并非列表抽象数据类型的一部分。
function List() { this.listSize = 0; this.pos = 0; this.dataStore = [];//建立一个空数组保存列表的元素 this.clear = clear; this.find = find; this.toString = toString; this.insert = insert; this.append = append; this.remove = remove; this.front = front; this.end = end; this.prev = prev; this.next = next; this.length = length; this.currPos = currPos; this.moveTo = moveTo; this.getElement = getElement; this.contains = contains; }
1.append: 给列表添加元素
咱们实现的第一个方法是append(),该方法给列表的下一个位置添加一个新的元素,这个位置恰好等于变量listSize的值。
function append(element) { this.dataStore[this.listSize++] = element; }
当新元素就位后,变量的listSize就增长1
2.remove:从列表中删除元素
接下来咱们看一下如何从列表中删除一个元素。remove()方法是List类中较难实现的一个方法。首先,要在列表中找到该元素,而后删除它。而且调整底层数组对象以填补删除元素后留下的空白。好消息是可使用splice()方法来简化这一过程,咱们先从一个辅助方法find()开始,该方法用于查找要删除的元素。
3.find在列表中查找某一元素
find()方法经过数组对象dataStore进行迭代,查找给定的元素。若是找到,就返回该元素的位置。若是找不到,返回-1.这是在数组中找不到元素时返回的标准值。咱们能够在remove()方法中利用此值作错误校验。
remove方法使用find()方法返回的位置对数组dataStore进行截取。数组改变后,将变量listSize的值减1。以反映列表的最新长度。若是元素删除成功,则返回true,不然返回false.
function remove(element) { var foundAt = this.find(element); if (foundAt > -1) { this.dataStore.splice(foundAt,1); --this.listSize; return true; } return false; }
4.length列表中有多少个元素
length()返回列表中元素的个数。
function length() { return this.listSize; }
5.toStirng:显示列表中的元素
如今能够建立一个方法,用来显示列表中元素。下面是一段简短的代码,实现了toString()方法。
function toString() { return this.dataStore; }
严格的来讲,此方法返回的是一个数组,而不是一个字符串,但它的目的是为了显示当前的一个状态,所以返回一个数组就足够了。
咱们暂时测试下目前实现的代码,检验咱们以前建立的方法。
var names = new List(); names.append('锤锤'); names.append('牛牛'); names.append('豆豆'); console.log(names.dataStore);//["锤锤", "牛牛", "豆豆"] names.remove('牛牛'); console.log(names.dataStore);//["锤锤", "豆豆"]
6.insert: 向列表中插入一个元素
接下来咱们要讨论的方法是insert()。若是在前面的列表中删除“牛牛”,如今又想将它放回原来的位置,该怎么办?insert()方法须要知道元素插入到什么位置(所以,如今的实现方法是假设插入到列表中某个元素以后,知道足额写后,就能够定义insert()方法啦)。
function insert(element, after) { var insertPos = this.find(after); if (insertPos > -1) { this.dataStore.splice(insertPos + 1, 0 ,element); ++this.listSize; return true } return false; }
实现的过程当中,insert()方法使用到find()方法,find()方法会找到传入的after参数在列表中的位置,找到该位置后,使用splice()获得将元素插入该位置后,而后将变量listSize加1并返回true.代表插入成功。
7.clear:清空列表中的全部元素。
接下来,咱们须要一个方法清空列表中全部元素,为插入新元素腾出空间。
function clear() { delete this.dataStore; this.dataStore = []; this.listSize = this.pos = 0; }
clear()方法使用delete操做符删除数组dataStore,接着建立新的空数组。最后一行将listSize和pos值设为0,代表是一个空的新列表。
8.断定给定值是否在列表中
当须要判断一个给定值是否在列表中时,contains()方法就变得颇有用。下面是该方法定义:
function contains(element) { for (var i = 0; i < this.dataStore.length; ++i) { if (this.dataStore[i] == element) { return true; } } return false; }
9.遍历列表
最后的一组方法容许用户在列表上自由移动,最后一个方法getElement()返回列表当前的元素
function front() { this.pos = 0; } function end() { this.pos = this.listSize - 1; } function prev() { if (this.pos > 0) { --this.pos; } } function next() { if (this.pos < this.listSize - 1) { ++this.pos; } } function currPos() { return this.pos; } function moveTo(position) { this.pos = position; } function getElement() { return this.dataStore[this.pos] }
如今咱们建立一个由姓名组成的列表,来展现怎么使用它。
var names = new List(); names.append('锤锤'); names.append('牛牛'); names.append('豆豆'); names.append('羊羊'); names.append('兔兔'); names.append('花花');
如今移动到列表中的第一个元素,而且显示它。
names.front(); console.log(names.getElement());//显示 “锤锤”
接下来向前移动一个单位并显示它:
names.next(); console.log(names.getElement()); //显示 “牛牛”
上面的代码展现这些行为其实是迭代器的概念,这也是接下来要讨论的内容。
3.使用迭代器访问列表
使用迭代器,能够没必要关系数据的内部存储方式。以实现对列表的遍历。前面提到的方法fornt(),end(),prev(),next()和currPos就实现了List类的一个迭代器。如下是和使用数组索引的方式相比,使用迭代器的一些优势。
了解了这些优势后,我使用一个迭代器遍历列表的例子:
for(names.front(); names.currPos() < names.length(); names.next()) { console.log(names.getElement()) }
在for循环的一开始,将列表中的当前位置设置为第一个元素。只要currPos的值小于列表的长度,就一直循环,每一次循环都调用next()方法将当前的位置向前移动一位。
同理,还能够从后向前遍历列表,代码以下:
for (names.front(); names.currPos() >= 0; names.prev()) { console.log(names.getElement()) }
循环从列表的最后一个元素开始,当 当前位置大于或等于0时,调用prev()方法后移一位。
迭代器只是用来在列表上随意移动,而不该该和人和为列表增长或删除元素的方法一块儿使用。
四,一个基于列表的应用。
1.读取文本
为了展现如何使用列表,咱们实现一个相似Redbox的租赁自助查询系统。
var _movies = "(1)RogueTrader(《魔鬼营业员》)NNN(2)TradingPlaces(《颠倒乾坤》)NNN(3)WallStreet(《华尔街》)NNN(4)WallStreet2:MoneyNeverSleeps(《华尔街2:金钱永不眠》)NNN(5)BoilerRoom(《开水房》)NNN(6)GlengarryGlen Ross(《拜金一族》)NNN(7)Enron:TheSmartestGuysInTheRoom(《安然风暴:屋内聪明人》)NNN(8)Trader(《交易员》)NNN(9)AmericanPsycho(《美国精神病人》)NNN(10)BonfireoftheVanities(《虚荣的篝火》)"; var movies = _movies.split("NNN");
上面的split()程序将字符串分割为数组,多一个空格,虽然多一个空格无伤大雅,可是在比较字符串时是个灾难,所以,咱们须要在循环里使用trim()方法删除每一个数组元素末尾的空格。要是有一个函数能将这些操做封装起来就再好不过了。
function createArr(file){ var arr = file.split("NNN"); for (var i = 0; i < arr.length; ++i) { arr[i] = arr[i].trim(); } return arr; }
2.使用列表管理影碟租赁
下面要将数组保存到一个列表,代码以下:
var movieList = new List(); for (var i = 0; i < movies.length; ++i) { movieList.append(movies[i]) }
如今能够写一个函数来显示影碟清单了:
function displayList(list) { for (list.front(); list.currPos() < list.length(); list.next()) { console.log(list.getElement()) } }
displayList()函数对于原生的数据类型没有什么问题,好比由字符串组成的列表。可是它用不了自定义类型。好比咱们将下面定义的Customer对象。让我对它稍微作修改,让它能够发现列表是由Customer对象组成的。这样就能够对应的进行显示了。下面是从新定义的displayList()函数:
function displayList(list) { for (list.front(); list.currPos() < list.length(); list.next()) { if (list.getElement() instanceof Customer) { console.log(list.getElement()['name'] + ", " + list.getElement()["movie"] ); } else { console.log(list.getElement()) } } }
对于列表中的每个元素,都使用instanceof操做符判断元素是不是Customer对象。若是是,就使用name和movie作索引。获得客户检出的值。若是不是,就返回该元素便可。
如今已经有了movies,还须要建立一个新的列表,Customers,用来保存在系统中检出电影的客户:
var customers = new List();
该列表包含Customer对象,该对象由用户的姓名和用户检出电影组成。下面是Costomer对象的构造函数。
function Customer(name, movie) { this.name = name; this.movie = movie; }
接下来,须要建立一个容许用户检出的电影函数。该函数有两个参数:客户端姓名和客户想要检出的电影。若是改电影目前能够租赁,该方法会从清单里删除该元素,同时加入客户列表customers.这个操做会用到列表的contains()方法。
下面是用于检出电影的函数:
function checkout(name, movie, filmList, customerList) { if (movieList.contains(movie)) { var c = new Customer(name, movie); customerList.append(c); filmList.remove(movie) } else { console.log(movie + "is not available") } }
下面使用简单的代码测试checkout函数
var movies = createArr(_movies); var movieList = new List(); var customers = new List(); for (var i = 0; i < movies.length; ++i) { movieList.append(movies[i]) } //显示xx displayList(movieList) checkout("RogueTrader","交易员",movieList,customers); //xx displayList(customers)
对于列表的方法,就更新至此,能够根据本身的须要,增长更健壮的方法去实现。
符:测试代码
function List() { this.listSize = 0; this.pos = 0; this.dataStore = [];//建立一个空数组保存列表的元素 this.clear = clear; this.find = find; this.toString = toString; this.insert = insert; this.append = append; this.remove = remove; this.front = front; this.end = end; this.prev = prev; this.next = next; this.length = length; this.currPos = currPos; this.moveTo = moveTo; this.getElement = getElement; this.contains = contains; } //给列表添加元素 function append(element) { this.dataStore[this.listSize++] = element; } //find查找方法 function find(element) { for (var i = 0;i < this.dataStore.length; ++i) { if (this.dataStore[i] == element) { return i; } } return -1; } //remove方法 function remove(element) { var foundAt = this.find(element); if (foundAt > -1) { this.dataStore.splice(foundAt,1); --this.listSize; return true; } return false; } //length function length() { return this.listSize; } //toString function toString() { return this.dataStore; } //insert function insert(element, after) { var insertPos = this.find(after); if (insertPos > -1) { this.dataStore.splice(insertPos + 1, 0 ,element); ++this.listSize; return true } return false; } //clear function clear() { delete this.dataStore; this.dataStore = []; this.listSize = this.pos = 0; } //contains判断给定值是否在列表中 function contains(element) { for (var i = 0; i < this.dataStore.length; ++i) { if (this.dataStore[i] == element) { return true; } } return false; } function front() { this.pos = 0; } function end() { this.pos = this.listSize - 1; } function prev() { if (this.pos > 0) { --this.pos; } } function next() { if (this.pos < this.listSize - 1) { ++this.pos; } } function currPos() { return this.pos; } function moveTo(position) { this.pos = position; } function getElement() { return this.dataStore[this.pos] } //var names = new List(); //names.append('锤锤'); //names.append('牛牛'); //names.append('豆豆'); //names.append('羊羊'); //names.append('兔兔'); //names.append('花花'); //console.log(names.dataStore); //names.front(); //console.log(names.getElement());//显示 “锤锤” //names.next(); //console.log(names.getElement()); //显示 “豆豆” // for(names.front(); names.currPos() < names.length(); names.next()) { // console.log(names.getElement()) // } var _movies = "(1)RogueTrader(《魔鬼营业员》) NNN(2)TradingPlaces(《颠倒乾坤》)NNN(3)WallStreet(《华尔街》)NNN(4)WallStreet2:MoneyNeverSleeps(《华尔街2:金钱永不眠》)NNN(5)BoilerRoom(《开水房》)NNN(6)GlengarryGlen Ross(《拜金一族》)NNN(7)Enron:TheSmartestGuysInTheRoom(《安然风暴:屋内聪明人》)NNN(8)Trader(《交易员》)NNN(9)AmericanPsycho(《美国精神病人》)NNN(10)BonfireoftheVanities(《虚荣的篝火》) "; function createArr(file){ var arr = file.split("NNN"); for (var i = 0; i < arr.length; ++i) { arr[i] = arr[i].trim(); } return arr; } var movies = createArr(_movies) //将数组保存到一个列表 var movieList = new List(); for (var i = 0; i < movies.length; ++i) { movieList.append(movies[i]) } //console.log(movieList) var customers = new List(); //显示影碟清单 function displayList(list) { for (list.front(); list.currPos() < list.length(); list.next()) { if (list.getElement() instanceof Customer) { console.log(list.getElement()['name'] + ", " + list.getElement()["movie"] ); } else { console.log(list.getElement()) } } } //displayList(movieList.dataStore) function Customer(name, movie) { this.name = name; this.movie = movie; } //检出电影的函数 function checkout(name, movie, filmList, customerList) { if (movieList.contains(movie)) { var c = new Customer(name, movie); customerList.append(c); filmList.remove(movie) } else { console.log(movie + "is not available") } } displayList(movieList)
(本章完结)
上一章:第二章:javascript: 数组 下一章:第四章:javascript: 栈