1、Map
1、 map诞生的原因
由于对象只接受字符串作为键名。使用受限
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。
2、 map的初始化
- new Map()
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o)//false
2.new Map([[a,b],[c,d]])
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
事实上,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构(详见《Iterator》一章)都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 Map。
const set = new Set([
['foo', 1],
['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
3、 属性和api
size
//下面都是原型方法
set(key,value)
get(key)
has(key)
delete(key)
clear() //清空
//返回的是iterator 需要next()或for of遍历
//注意区分Object.keys(obj) Object.values(obj) Object.entries(obj) 返回是数组,不是iterator
keys()
values()
entries()
forEach()
4、 keys、values、entries 迭代器
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
上面代码最后的那个例子,表示 Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。
map[Symbol.iterator] === map.entries
// true
5、 Map与数组的转换、Map与Object转换和遍历
//转数组 用解构最快
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
// map转对象
for of 遍历
//0bject转Map,Object静态方法entries(obj)将对象转为二位数组,然后给Map构造函数
new Map(Object.entries(obj))
//
结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有map和filter方法)。
const map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
const map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 产生 Map 结构 {1 => 'a', 2 => 'b'}
const map2 = new Map(
[...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 产生 Map 结构 {2 => '_a', 4 => '_b', 6 => '_c'}
map 提供的forEach也可以
map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
//json与map
6、 实践
定义一个最近最久未使用(LRU)类
//Map、 Set 遍历的顺序是插入的顺序
class LRUCache {
constructor(capacity) {
this.capacity = capacity
this.map = new Map();
}
get(key) {
let val = this.map.get(key);
if (val === undefined) return -1;
this.map.delete(key); // 因为被用过一次,原有位置删除
this.map.set(key, val); // 放入最下面表示最新使用
return val;
}
put(key, val) {
if (this.map.has(key)) this.map.delete(key); // 如果有,删除
this.map.set(key, val); // 放到最下面表示最新使用
if (this.map.size > this.capacity) {
// 这里有个知识点
// map的entries方法,还有keys方法(可以看mdn)),会返回一个迭代器
// 迭代器调用next也是顺序返回,所以返回第一个的值就是最老的,找到并删除即可
this.map.delete(this.map.entries().next().value[0])
}
}
}
//执行
var lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
2、 Set
1、定义
Set类似于数组,但是成员的值都是唯一的,没有重复的值。
//1、add 添加
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
//数组或迭代器添加
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
new Set('abcdaa')
2、api
基本类似Map,不在描述
3、与数组转换
// 去除数组的重复成员
[...new Set(array)]
[...new Set('ababbc')].join('')
// "abc"
Array.from(new Set([1, 2, 3, 4, 5]),x=>x*x)
4、遍历
//1) forEach
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
//2) for...of
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
//3) 结合数组的map、filter
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
4)改变原set接口,只能生城新的set。
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6
// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
5、实践
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
var detectCycle = function (head) {
const visited = new Set();
while (head !== null) {
if (visited.has(head)) {
return head;
}
visited.add(head);
head = head.next;
}
return null;
};