在平常生活中,人們常用列表:待辦事項列表,購物清單,十佳榜單,最後十名榜單等。計算機也在使用列表,尤爲是列表中元素保存的是太多時。當不須要一個很長的序列中查找元素,或對其進行排序時,列表顯得尤其有用。反之,若是數據結構很是複雜,列表的做用就沒有那麼大了。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: 棧