资讯专栏INFORMATION COLUMN

Java™ 教程(Set接口)

Apollo / 837人阅读

Set接口

Set是一个不能包含重复元素的Collection,它模拟了数学集抽象,Set接口仅包含从Collection继承的方法,并添加禁止重复元素的限制,Set还为equalshashCode操作的行为添加了一个更强的契约,允许Set实例有意义地进行比较,即使它们的实现类型不同,如果两个Set实例包含相同的元素,则它们是相等的。

Java平台包含三个通用的Set实现:HashSetTreeSetLinkedHashSet。将其元素存储在哈希表中的HashSet是性能最佳的实现,但它不能保证迭代的顺序。TreeSet将其元素存储在红黑树中,根据元素的值对其元素进行排序,它比HashSet慢得多。LinkedHashSet实现为哈希表,其中有一个链表,根据它们插入集合的顺序(插入顺序)对其元素进行排序,LinkedHashSet让它的客户端避免了HashSet提供的未指定的、通常混乱的排序,但代价只稍微高一点。

这是一个简单但有用的Set语法,假设你有一个Collectionc,并且你想要创建另一个包含相同元素的Collection,但会删除所有重复项,下面的一行代码就可以解决这个问题。

Collection noDups = new HashSet(c);

它的工作原理是创建一个Set(根据定义,它不能包含重复项),初始化包含c中的所有元素,它使用Collection接口部分中描述的标准转换构造函数。

或者,如果使用JDK 8或更高版本,你可以使用聚合操作轻松收集到Set

c.stream()
.collect(Collectors.toSet()); // no duplicates

这是一个稍长的示例,它将名称Collection累积到TreeSet中:

Set set = people.stream()
.map(Person::getName)
.collect(Collectors.toCollection(TreeSet::new));

以下是第一个语法的次要变体,它在删除重复元素时保留了原始集合的顺序:

Collection noDups = new LinkedHashSet(c);

以下是封装前面的语法的泛型方法,返回与传递的相同的泛型类型的Set

public static  Set removeDups(Collection c) {
    return new LinkedHashSet(c);
}
Set接口基础操作

size操作返回Set中的元素数(其基数),isEmpty方法完全符合你的想法,add方法将指定的元素添加到Set(如果它尚不存在)并返回一个布尔值,指示是否添加了元素。类似地,remove方法从Set中删除指定的元素(如果存在)并返回一个布尔值,指示元素是否存在,iterator方法在Set上返回Iterator

以下程序打印出其参数列表中的所有不同单词,提供了该程序的两个版本,第一个使用JDK 8聚合操作,第二个使用for-each构造。

使用JDK 8聚合操作:

import java.util.*;
import java.util.stream.*;

public class FindDups {
    public static void main(String[] args) {
        Set distinctWords = Arrays.asList(args).stream()
        .collect(Collectors.toSet()); 
        System.out.println(distinctWords.size()+ 
                           " distinct words: " + 
                           distinctWords);
    }
}

使用for-each构造:

import java.util.*;

public class FindDups {
    public static void main(String[] args) {
        Set s = new HashSet();
        for (String a : args)
               s.add(a);
               System.out.println(s.size() + " distinct words: " + s);
    }
}

现在运行该程序的任一版本。

java FindDups i came i saw i left

生成以下输出:

4 distinct words: [left, came, saw, i]

请注意,代码始终引用Collection通过其接口类型(Set)而不是其实现类型,这是一个强烈推荐的编程实践,因为它使你可以灵活地仅通过更改构造函数来更改实现。如果用于存储集合的变量或用于传递它的参数中的任何一个被声明为Collection的实现类型而不是其接口类型,必须更改所有这些变量和参数才能更改其实现类型。

此外,无法保证生成的程序能够正常运行,如果程序使用原始实现类型中存在但未在新实现类型中存在的任何非标准操作,则程序将失败,仅通过其接口引用集合可防止你使用任何非标准操作。

前面示例中Set的实现类型是HashSet,它不保证Set中元素的顺序,如果你希望程序按字母顺序打印单词列表,只需将Set的实现类型从HashSet更改为TreeSet,进行这个简单的单行更改会导致前一个示例中的命令行生成以下输出。

java FindDups i came i saw i left

4 distinct words: [came, i, left, saw]
Set接口批量操作

批量操作特别适合于Set,应用时,它们执行标准的集代数运算,假设s1s2Set,批量操作是这样做的:

s1.containsAll(s2) — 如果s2s1的子集,则返回true(如果set s1包含s2中的所有元素,则s2s1的子集)。

s1.addAll(s2) — 将s1转换为s1s2的并集(两个集合的并集是包含任一集合中包含的所有元素的集合)。

s1.retainAll(s2) — 将s1转换为s1s2的交集(两个集合的交集是仅包含两个集合共有的元素的集合)。

s1.removeAll(s2) — 将s1转换为s1s2的(非对称)差集(例如,s1s2的差集就是包含s1中所有元素但不包含s2中的所有元素的集)。

若要非破坏性地计算两个集合的并集、交集或差集(不修改任何一个集合),调用者必须在调用适当的批量操作之前复制一个集合,以下是由此产生的语法。

Set union = new HashSet(s1);
union.addAll(s2);

Set intersection = new HashSet(s1);
intersection.retainAll(s2);

Set difference = new HashSet(s1);
difference.removeAll(s2);

前面的语法中的结果集的实现类型是HashSet,如前所述,它是Java平台中最好的全能Set实现,但是,任何通用的Set实现都可以替代。

让我们重温一下FindDups程序,假设你想知道参数列表中的哪些单词只出现一次,哪些单词出现多次,但你不希望重复打印出任何重复项,这种效果可以通过生成两个集合来实现 — 一个集合包含参数列表中的每个单词,另一个集合仅包含重复项。仅出现一次的单词是这两组的差集,我们知道如何计算,以下是生成的程序的样子。

import java.util.*;

public class FindDups2 {
    public static void main(String[] args) {
        Set uniques = new HashSet();
        Set dups    = new HashSet();

        for (String a : args)
            if (!uniques.add(a))
                dups.add(a);

        // Destructive set-difference
        uniques.removeAll(dups);

        System.out.println("Unique words:    " + uniques);
        System.out.println("Duplicate words: " + dups);
    }
}

当使用前面使用的相同参数列表运行时(i came i saw i left),程序产生以下输出。

Unique words:    [left, saw, came]
Duplicate words: [i]

不太常见的集代数运算是对称差集 — 包含在两个指定集合中但不同时包含在两个集合中的元素的集合,以下代码非破坏性地计算两个集合的对称差集。

Set symmetricDiff = new HashSet(s1);
symmetricDiff.addAll(s2);
Set tmp = new HashSet(s1);
tmp.retainAll(s2);
symmetricDiff.removeAll(tmp);
Set接口数组操作

除了对其他任何Collection执行的操作之外,数组操作不会对Set执行任何特殊操作,Collection接口部分介绍了这些操作。

上一篇:Collection接口 下一篇:List接口

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73096.html

相关文章

  • Java教程(集合接口

    集合接口 核心集合接口封装了不同类型的集合,如下图所示,这些接口允许独立于其表示的细节来操纵集合,核心集合接口是Java集合框架的基础,如下图所示,核心集合接口形成层次结构。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一种特殊的Collection,SortedSet是一种特殊的Set,依此类推,另请注意,层次结构...

    elisa.yang 评论0 收藏0
  • Java教程(List接口

    List接口 List是一个有序的Collection(有时称为序列),列表可能包含重复元素,除了从Collection继承的操作之外,List接口还包括以下操作: 位置访问 — 根据列表中的数字位置操纵元素,这包括get、set、add、addAll和remove等方法。 搜索 — 搜索列表中的指定对象并返回其数字位置,搜索方法包括indexOf和lastIndexOf。 迭代 — 扩展Ite...

    hedzr 评论0 收藏0
  • Java教程(SortedSet接口

    SortedSet接口 SortedSet是一个Set,它按升序维护其元素,根据元素的自然顺序或根据SortedSet创建时提供的Comparator进行排序,除了常规的Set操作外,SortedSet接口还提供以下操作: 范围视图 — 允许对已排序集进行任意范围操作。 端点 — 返回有序集合中的第一个或最后一个元素。 比较器访问 — 返回用于对集合进行排序的Comparator(如果有)。 ...

    amc 评论0 收藏0
  • Java教程(Map接口

    Map接口 Map是将键映射到值的对象,map不能包含重复的键:每个键最多可以映射一个值,它模拟数学函数抽象。Map接口包括基本操作的方法(如put、get、remove、containsKey、containsValue、size和empty),批量操作(如putAll和clear)和集合视图(如keySet、entrySet和values)。 Java平台包含三个通用Map实现:HashMap...

    Kahn 评论0 收藏0
  • Java教程(泛型类型)

    泛型类型 泛型类型是通过类型参数化的泛型类或接口,修改以下Box类以演示此概念。 一个简单的Box类 首先检查一个对任何类型的对象进行操作的非泛型Box类,它只需要提供两个方法:set,它将一个对象添加到box中,get,它将检索它: public class Box { private Object object; public void set(Object object) ...

    Crazy_Coder 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<