ES6 中的 Set

作者:kurtshen

ES6 新增了几种集合类型,本文主要介绍Set以及其使用。

其基本描述为:

  • Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

它的声明:

  • new Set([iterable]); 其中iterable是一个可迭代对象,其中的所有元素都会被加入到 Set 中。null被视作 undefined。也可以不传入[iterable],通过其add方法来添加元素。

对于ruby或者是python比较熟悉的同学可能会比较了解set这个东东。它是ES6 新增的有序列表集合,它不会包含重复项。

Set的属性

  • Set.prototype.size:返回Set实例的成员数量。
  • Set.prototype.constructor:默认的构造Set函数。

Set方法

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除成功。
  • has(value):返回一个布尔值,表示参数是否为Set的成员。
  • clear():清除所有成员,没有返回值。
  • keys() :返回一个键名的遍历器
  • values() :返回一个值的遍历器
  • entries() :返回一个键值对的遍历器
  • forEach():使用回调函数遍历每个成员

例子

先借用之前看过的一篇英文blog的例子。地址请戳Removing Elements from JavaScript Arrays

总所周知,数组是没有remove这个方法的。当我们需要从一个数组里面移除一个特定的元素时,我们通常会怎么写?

在es6之前,我们会这么写

function remove(array, element) {
    const index = array.indexOf(element);
    array.splice(index, 1);
}

然后我们可以这么用

const arr = ["a", "e", "i", "o", "u", "x"];
arr; //["a", "e", "i", "o", "u", "x"]

// 移除其中的“x”
remove(arr, "x");
arr; // ["a", "e", "i", "o", "u"]

// 细心的同学会发现我们前面那么写的问题,如果我们再次移除“x”的话,会发生移除最后一个元素
remove(arr, "x");
arr; // ["a", "e", "i", "o"]

当数组查找不到某元素时会返回-1,则数组的splice会从末尾往前,移除了最后一个元素,于是我们会这么写

function remove(array, element) {
    const index = array.indexOf(element);

    if (index !== -1) {
        array.splice(index, 1);
    }
}

这样的话我们就每次总是需要去检测index的值。

我们还可以用filter来写remove,这样则返回一个新的数组

function remove(array, element) {
    return array.filter(e => e !== element);
}

那么有了Set我们能怎写?其实也不需要写,因为set其初始化可以接受一个数组,作为构造参数,另外自带了一个delete的方法

const set = new Set(["a", "e", "i", "o", "u", "x"]);
set.delete("x"); // true
set; // Set {"a", "e", "i", "o", "u"}

set.delete("x"); // false
set; // Set {"a", "e", "i", "o", "u"}

好像蛮好的,但其实Set集合中的值是不能重复的,如果所需要的数据结构是要允许有重复项的,那么Set也没有什么用。

Set中值的相等是这么说的

  • 因为 Set 中的值总是唯一的,所以需要判断两个值是否相等。判断相等的算法与严格相等(===操作符)不同。具体来说,对于 Set , +0 (+0 严格相等于-0)和-0是不同的值。尽管在最新的 ECMAScript 6规范中这点已被更改。从Gecko 29.0和 recent nightly Chrome开始,Set 视 +0 和 -0 为相同的值。另外,NaN和undefined都可以被存储在Set 中, NaN之间被视为相同的值(尽管 NaN !== NaN)。

另一个例子

既然它的值是唯一的,那么我们是不是可以用它来实现数组去重?

原先我们去重可能会这么写

let arr = [1,'1', 2, 3, 2, 4, 5, 4, 1];
let arr_unique = arr.filter(function(item, index, array) {
return array.indexOf(item, index + 1) === -1;
});
arr_unique;//["1", 3, 2, 5, 4, 1]

或者利用对象key的唯一性,这么写

let arr = [1,'1', 2, 3, 2, 4, 5, 4, 1];
let tmpObj = {};
let arr_unique = [];
arr.forEach(function(a) {
  let key = (typeof a) + a;
  if (!tmpObj[key]) {
    tmpObj[key] = true;
    arr_unique.push(a);
  }
});
arr_unique;//[1, "1", 2, 3, 4, 5]

于是现在还能这么写

let arr = [1,'1', 2, 3, 2, 4, 5, 4, 1];
let set = new Set(arr);
let arr_unique = Array.from(set);//Array新增了一个静态方法Array.from,可以把类似数组的对象转换为数组
arr_unique;//[1, "1", 2, 3, 4, 5]

除了Array.from,我们也可以这么转化数组

let set = new Set(['a','b','c']);
let arr = [...set];
arr;//['a','b','c']

而利用Array与Set的相互转化,还可以很容易地实现并集(Union)和交集(Intersect)

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
let union = new Set([...a, ...b]);
union;// [1, 2, 3, 4]
let intersect = new Set([...a].filter(x => b.has(x)));
intersect;// [2, 3]

总结

与Array相比:

  • Set中存储的元素是唯一的,而Array中可以存储重复的元素。
  • Set中遍历元素的方式:Set通过for…of…,而Array通过for…in…。
  • Set是集合,不能像数组用下标取值。

原文链接:http://ivweb.io/topic/582925cd9554d860548c1fa3

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LanceToBigData

JavaSE(六)包装类、基本类型和字符串之间的转换、==和equals的区别

一、包装类 Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足, 在设计类时为每个基...

1657
来自专栏java一日一条

Java ArrayList的不同排序方法

由于其功能性和灵活性,ArrayList是 Java 集合框架中使用最为普遍的集合类之一。ArrayList 是一种 List 实现,它的内部用一个动态数组来存...

482
来自专栏老马说编程

(39) 剖析LinkedList / 计算机程序的思维逻辑

上节我们介绍了ArrayList,ArrayList随机访问效率很高,但插入和删除性能比较低,我们提到了同样实现了List接口的LinkedList,它的特点与...

1878
来自专栏lgp20151222

Class.forName()用法详解

主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,...

751
来自专栏Java Edge

Java中正则表达式PatternMatcherStringJava String.split()用法小结

2675
来自专栏面朝大海春暖花开

java泛型理解。代码更明了。

在工作中,相对于现有的项目源码的数据库操作层,无论是mybatis,hibernate或者是自己封装的baseModel层,都会使用到泛型。

522
来自专栏老马说编程

(84) 反射 / 计算机程序的思维逻辑

上节介绍完了并发,从本节开始,我们来探讨Java中的一些动态特性,包括反射、类加载器、注解和动态代理等。利用这些特性,可以以优雅的方式实现一些灵活和通用的功能,...

2208
来自专栏小灰灰

JDK容器学习之Queue: ArrayDeque

数组双端队列 ArrayDeque 双端队列,表示可以添加元素到(或删除,获取)队列头也可以添加元素到(或删除,获取)队列尾 ? 1. 底层数据结构 类中定义成...

1726
来自专栏钟绍威的专栏

SpringMVC类型转换器、属性编辑器PropertiesEditor源码分析CustomDateEditor源码分析TypeConverterDelegate源码分析

对于MVC框架,参数绑定一直觉得是很神奇很方便的一个东西,在参数绑定的过程中利用了属性编辑器、类型转换器 参数绑定流程 参数绑定:把请求中的数据,转化成指定类型...

1918
来自专栏weixuqin 的专栏

JDBC技术

任何一种数据库驱动程序都提供一个 java.sql.Driver 接口的驱动类,在加载某个数据库驱动程序的驱动类时,都创建自己的实例对象并向 java.sql....

922

扫码关注云+社区