第三章:javascript: 列表

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

  1. 訪問列表元素時沒必要關心底層的數據存儲結構
  2. 當爲列表添加一個元素時,索引的值就不對了,此時只用更新列表,而不用更新迭代器。
  3. 能夠用不一樣類型的數據存儲方式實現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: 棧

相關文章
相關標籤/搜索