什么是列表?
列表是一组有序的数据。每个列表中的数据项称为元素。在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
217

被折叠的 条评论
为什么被折叠?



