什么是列表?
列表是一組有序的數據。每個列表中的數據項稱為元素。在JavaScript中,列表中的元素可以是任意類型的數據。列表中可以保存多少元素沒有確定,實際使用時元素的數量受到程序內存的限制。
現在我們來抽象一下列表的抽象數據類型定義
listSize(屬性):列表中的元素個數
pos(屬性):列表中的元素當前可訪問的位置(位置指針)
length(方法):返回列表中元素的個數
clear(方法):清空列表中的所有元素
toString(方法):返回列表的字符串形式
getElement(方法):返回列表當前可訪問位置對應的元素
insert(方法):在現有元素后面插入新元素
append(方法):在列表的末尾添加新元素
remove(方法):從列表中移除元素
front(方法):將列表的當前位置移動到第一個元素
end(方法):將列表的當前位置移動到最后一個元素
prev(方法):將列表的當前位置前移一位
next(方法):將列表的當前位置后移一位
hasNext(方法):判斷列表在當前位置是否還有一下個元素
hasPrev(方法):判斷列表在當前位置是否還有上一個元素
currPos(方法):返回列表的當前位置
moveTo(方法):將列表的當前位置移動到指定位置
上面抽象了列表的數據類型,包括要使用到的屬性和方法,下面使用代碼來實現一個列表類:
// 定義列表類 class List { constructor() { // 定義列表的元素個數 this.listSize = 0; // 列表的位置指針 this.pos = 0; // 列表的數據存儲 this.dataSource = []; } // append: 列表增加元素 append(element) { this.dataSource[this.listSize++] = element; } // remove: 列表中刪除元素 remove(element) { var findAt = this.find(element); if (findAt > -1) { this.dataSource.splice(findAt, 1); --this.listSize; return true; } return false; } // find:輔助方法,用於查找要操作的元素 find(element) { for (var i = 0; i < this.listSize; i++) { if (this.dataSource[i] === element) { return i; } } return -1; } // length:返回列表中的元素個數 length() { return this.listSize; } // toString: 返回列表的字符串形式 toString() { return this.dataSource.toString(); } // insert: 向列表中添加一個元素 insert(element, after) { var insertPos = this.find(after); if (insertPos > -1) { this.dataSource.splice(insertPos + 1, 0, element); ++this.listSize; return true; } return false; } // clear: 清空列表中的元素 clear() { this.dataSource.length = 0; this.listSize = this.pos = 0; } // front:指針歸零(移動到列表的第一個元素的位置) front() { this.pos = 0; } // end: 指針移動到列表的最后一個元素的位置 end() { this.pos = this.listSize - 1; } // hasPrev: 判斷指針是否可以向前移動 hasPrev() { return this.pos > 0 } // hasNext: 判斷指針是否可以向后移動 hasNext() { return this.pos < this.listSize - 1 } // moveTo: 修改指針的位置 moveTo(position) { if (0 <= position && position <= this.listSize - 1) { this.pos = position; } } // prev: 指針向前移動一位 prev() { if (this.hasPrev()) { --this.pos; } } // next: 指針向后移動一位 next() { if (this.hasNext()) { ++this.pos; } } // getElement: 獲取列表中指針所對應的元素 getElement() { return this.dataSource[this.pos]; } // currPos: 返回當前指針位置 currPos() { return this.pos; } }
其中next,prev,moveTo,front,end是我們設置的一些迭代器,使用迭代器有如下好處:
1. 訪問列表元素的時候,我們不必關心底層的數據存儲結構。
2.當對列表進行刪除或增加操作的時候,存儲列表元素的數組的索引值就更新了,此時只用更新列表,不需要更新迭代器。
3.可以用不同類型的數據存儲方式來實現List類,迭代器為訪問列表中的元素提供了一種統一的方式。
這樣子我們就是實現了上面抽象的列表數據結構,接下來我們用它在做些什么。
創建一個Person 類,該類用於保存人的姓名和性別信息。創建一個至少包含10個Person對象的列表。編寫一個函數顯示列表中所有擁有相同性別的人。
分析如下,我們之前創建的列表類里面只適合操作基本數據類型,現在列表中的元素很明顯是引用類型,所以我們想到的編寫一個新的列表類,它擁有List類的所有方法,同時對有些方法實現覆蓋,還要增加一些新的方法,以達到自己的需求。(之前定義的List類只是一個基本的模型而已,方便在實際應用中提供基礎方法,自己實現相關的擴展)
代碼如下:
// 定義列表類 class List { constructor() { // 定義列表的元素個數 this.listSize = 0; // 列表的位置指針 this.pos = 0; // 列表的數據存儲 this.dataSource = []; } // append: 列表增加元素 append(element) { this.dataSource[this.listSize++] = element; } // remove: 列表中刪除元素 remove(element) { var findAt = this.find(element); if (findAt > -1) { this.dataSource.splice(findAt, 1); --this.listSize; return true; } return false; } // find:輔助方法,用於查找要操作的元素 find(element) { for (var i = 0; i < this.listSize; i++) { if (this.dataSource[i] === element) { return i; } } return -1; } // length:返回列表中的元素個數 length() { return this.listSize; } // toString: 返回列表的字符串形式 toString() { return this.dataSource.toString(); } // insert: 向列表中添加一個元素 insert(element, after) { var insertPos = this.find(after); if (insertPos > -1) { this.dataSource.splice(insertPos + 1, 0, element); ++this.listSize; return true; } return false; } // clear: 清空列表中的元素 clear() { this.dataSource.length = 0; this.listSize = this.pos = 0; } // front:指針歸零(移動到列表的第一個元素的位置) front() { this.pos = 0; } // end: 指針移動到列表的最后一個元素的位置 end() { this.pos = this.listSize - 1; } // hasPrev: 判斷指針是否可以向前移動 hasPrev() { return this.pos > 0 } // hasNext: 判斷指針是否可以向后移動 hasNext() { return this.pos < this.listSize - 1 } // moveTo: 修改指針的位置 moveTo(position) { if (0 <= position && position <= this.listSize - 1) { this.pos = position; } } // prev: 指針向前移動一位 prev() { if (this.hasPrev()) { --this.pos; } } // next: 指針向后移動一位 next() { if (this.hasNext()) { ++this.pos; } } // getElement: 獲取列表中指針所對應的元素 getElement() { return this.dataSource[this.pos]; } // currPos: 返回當前指針位置 currPos() { return this.pos; } } // 創建Person類 class Person { constructor(name, sex) { this.name = name; this.sex = sex; } } class PersonList extends List { // 重寫getElement方法 getElement() { return this.dataSource[this.pos].name; } // 返回指定性別人員集合 displayNames(sex){ return this.dataSource.filter(person=>person.sex === sex); } }; // 列表裝載 const personList = new PersonList(); for (let i = 0; i < 10; i++) { personList.append(new Person('a' + i, Math.random() > 0.5 ? '男' : '女')); } console.log(personList.getElement()); // a0 personList.end(); console.log(personList.getElement());// a9 console.log(personList); console.log('personList列表中性別為男的人員組合為',personList.displayNames('男'));
這樣子我們就實現了需求,你可能會說需要這么麻煩,創建一個函數統計一下指定性別的人員就行了,為什么還要這些操作。其實在這里我們練習的是列表的使用,在這里只是舉一個列子,其中滋味,自己體會。
有了之前的List類,我們處理一些相關的問題,通過簡單的繼承,就會變得相當簡單。
我覺得學習新的知識之后,一定要用到實際開發中去,不然你學與不學有什么區別了,無非是浪費了一些時間來安慰自己罷了。
源碼和案例地址:https://gitee.com/mvc_ydb/data-structure/blob/master/list.js