Scala集合:Set

Set 是一个不重复元素的无序集合。它不包含重复元素。此外,它不允许通过索引访问一个元素,因为它并没有索引。

下面是一个Set 的例子:

val fruits = Set("apple", "orange", "pear", "banana")
val set1 = Set(1,2,3,2,3,4,5,5)

Sets 支持两个基本操作:

  • contains:如果set 包含输入参数,则返回true;
  • isEmpty:如果set 是空的,返回true;

Set是不重复元素的集合。尝试将已有元素加入进来是没有效果的。比如:

(Set(2,0,1) + 1).foreach(println)

【示例】学习如何使用Scala的不可变Set集合,并执行一些常见的操作,如初始化,添加元素,添加集合,集合的差集和交集,以及创建空集合。

/**
  * Set是一种数据结构,它允许存储一些值,但这些值不能重复。
  */
object SetDemo {
  def main(args: Array[String]): Unit = {
    println("1: 初始化一个有3个元素的集合")
    val set1: Set[String] = Set("苹果","香蕉","葡萄干","苹果")
    println(s"set1的元素 = $set1")

    println("\n2: 检查集合中存在的特定元素")
    println(s"是否有苹果 = ${set1("苹果")}")
    println(s"是否有香蕉 = ${set1("香蕉")}")
    println(s"是否有葡萄干 = ${set1("葡萄干")}")
    println(s"是否有火龙果 = ${set1("火龙果")}")

    println("\n3: 使用+ 在Set 中添加元素")
    val set2: Set[String] = set1 + "火龙果" + "葡萄干"
    println(s"使用 + 向Set集合中添加元素 = $set2")

    println("\n4: 使用 ++ 将两个Set集合添加到一起")
    val set3: Set[String] = set1 ++ Set[String]("火龙果", "葡萄干")
    println(s"使用 ++ 将两个Set集合添加到一起 = $set3")

    println("\n5: 使用 - 从Set 中删除元素")
    val set4: Set[String] = set1 - "苹果"
    println(s"去掉苹果后的Set集合元素 = $set4")

    println("\n6: 使用& 找到两个集合之间的交集")
    val set5: Set[String] = Set("火龙果", "香蕉", "苹果")
    println(s"set1和set5的交集 = ${set1 & set5}")

    println("\n7: 使用 &~ 找出两个集合的差集")
    println(s"set1和set5的差集 = ${set1 &~ set5}")

    println("\n8: 初始化一个空集")
    val emptySet: Set[String] = Set.empty[String]
    println(s"Empty Set = $emptySet")
  }
}

Set是一种数据结构,它允许存储一些值,但这些值不能重复。Set不保留元素插入的顺序。默认情况下,Set是以HashSet实现的,其元素根据hashCode方法的值进行组织。

Set(1,2,3,5,7,8).foreach(println)

【示例】下面是使用HashSet的示例。

在Scala中,HashSet是Set语义的具体实现。HashSet将使用元素的hashCode作为键,以便在HashSet中快速查找元素的值。学习如何使用Scala的不可变HashSet,并执行一些常见的操作,如初始化、添加元素、添加HashSet、HashSet差集和交集、以及创建空HashSet。

Scala中HashSet的内部数据结构是一个HashTrie,如果你有Java背景,会发现这与Java中的HashTable是不同的。

import scala.collection.immutable.HashSet

object HashSetDemo {
  def main(args: Array[String]): Unit = {
    println("1: 初始化一个有3个元素的集合")
    val hashSet1: HashSet[String] = HashSet("苹果","香蕉","葡萄干","苹果")
    println(s"hashSet1的元素 = $hashSet1")

    println("\n2: 检查集合中存在的特定元素")
    println(s"是否有苹果 = ${hashSet1("苹果")}")
    println(s"是否有香蕉 = ${hashSet1("香蕉")}")
    println(s"是否有葡萄干 = ${hashSet1("葡萄干")}")
    println(s"是否有火龙果 = ${hashSet1("火龙果")}")

    println("\n3: 使用+ 在HashSet 中添加元素")
    val hashSet2: HashSet[String] = hashSet1 + "火龙果" + "葡萄干"
    println(s"使用 + 向Set集合中添加元素 = $hashSet2")

    println("\n4: 使用 ++ 将两个Set集合添加到一起")
    val hashSet3: HashSet[String] = hashSet1 ++ HashSet[String]("火龙果", "葡萄干")
    println(s"使用 ++ 将两个Set集合添加到一起 = $hashSet3")

    println("\n5: 使用 - 从Set 中删除元素")
    val hashSet4: HashSet[String] = hashSet1 - "苹果"
    println(s"去掉苹果后的Set集合元素 = $hashSet4")

    println("\n6: 使用& 找到两个集合之间的交集")
    val hashSet5: HashSet[String] = HashSet("火龙果", "香蕉", "苹果")
    println(s"set1和set5的交集 = ${hashSet1 & hashSet5}")

    println("\n7: 使用 &~ 找出两个集合的差集")
    println(s"hashSet1和hashSet5的差集 = ${hashSet1 &~ hashSet5}")

    println("\n8: 初始化一个空集")
    val emptyHashSet: HashSet[String] = HashSet.empty[String]
    println(s"Empty Set = $emptyHashSet")
  }
}

LinkedHashSet可以记住元素被插入的顺序。它会维护一个链表来达到这个目的。

val weeks= scala.collection.mutable.LinkedHashSet("Mo","Tu","We","Th","Fr")
weeks.foreach(println)

继之前关于HashSet和Set的之后,TreeSet允许存储惟一的元素,但也提供了元素的排序。根据Scala文档,TreeSet内部使用了一个Red-Back数据结构来确保一个平衡的树,为大多数操作提供O(log n)。

【示例】学习如何使用Scala的不可变TreeSet,并执行一些常见的操作,如初始化、添加元素、添加TreeSet、TreeSet差集和交集、排序、创建空的TreeSet。

import scala.collection.immutable.TreeSet

/**
  * 由于正在处理Set语义,因此只存储唯一的和不可重复的值。
  */
object TreeSetDemo {
  def main(args: Array[String]): Unit = {
    println("1: 初始化一个有3个元素的集合")
    val treeSet1: TreeSet[String] = TreeSet("苹果","香蕉","葡萄干","苹果")
    println(s"treeSet1的元素 = $treeSet1")

    println("\n2: 检查集合中存在的特定元素")
    println(s"是否有苹果 = ${treeSet1("苹果")}")
    println(s"是否有香蕉 = ${treeSet1("香蕉")}")
    println(s"是否有葡萄干 = ${treeSet1("葡萄干")}")
    println(s"是否有火龙果 = ${treeSet1("火龙果")}")

    println("\n3: 使用+ 在TreeSet 中添加元素")
    val treeSet2: TreeSet[String] = treeSet1 + "火龙果" + "葡萄干"
    println(s"使用 + 向TreeSet集合中添加元素 = $treeSet2")

    println("\n4: 使用 ++ 将两个Set集合添加到一起")
    val treeSet3: TreeSet[String] = treeSet1 ++ TreeSet[String]("火龙果", "葡萄干")
    println(s"使用 ++ 将两个TreeSet集合添加到一起 = $treeSet3")

    println("\n5: 使用 - 从TreeSet 中删除元素")
    val treeSet4: TreeSet[String] = treeSet1 - "苹果"
    println(s"去掉苹果后的Set集合元素 = $treeSet4")

    println("\n6: 使用& 找到两个集合之间的交集")
    val treeSet5: TreeSet[String] = TreeSet("火龙果", "香蕉", "苹果")
    println(s"treeSet1和treeSet5的交集 = ${treeSet1 & treeSet5}")

    println("\n7: 使用 &~ 找出两个集合的差集")
    println(s"treeSet1和treeSet5的差集 = ${treeSet1 &~ treeSet5}")

    println("\n8: 改变TreeSet的顺序为降序字母排列")
    object AlphabetOrdering extends Ordering[String] {
      def compare(element1:String, element2:String): Int = element2.compareTo(element1)
    }
    val treeSet6: TreeSet[String] = TreeSet("苹果","香蕉","葡萄干")(AlphabetOrdering)
    println(s"treeSet6的元素 = $treeSet6")

    println("\n9: 初始化一个空集")
    val emptyTreeSet: TreeSet[String] = TreeSet.empty[String]
    println(s"Empty TreeSet = $emptyTreeSet")
  }
}

如果想要按照已排序的顺序来访问其中的元素,使用SortedSet:

scala.collection.immutable.SortedSet(1,2,3,4,5,6).foreach(println)

继前面关于Set、HashSet和TreeSet之后,SortedSet允许存储惟一的元素,但也提供了元素的排序。根据Scala文档,SortedSet是一种特征(trait)。它的具体类实现是讨论过的TreeSet。

【示例】学习如何使用Scala的不可变SortedSet,并执行一些常见的操作,如初始化、添加元素、添加SortedSet、SortedSet的差集和交集、排序和创建空的SortedSet。

import scala.collection.immutable.SortedSet

object SortedSetDemo {
  def main(args: Array[String]): Unit = {
    println("1: 初始化一个有3个元素的集合")
    val sortedSet1: SortedSet[String] = SortedSet("苹果","香蕉","葡萄干","苹果")
    println(s"sortedSet1的元素 = $sortedSet1")

    println("\n2: 检查集合中存在的特定元素")
    println(s"是否有苹果 = ${sortedSet1("苹果")}")
    println(s"是否有香蕉 = ${sortedSet1("香蕉")}")
    println(s"是否有葡萄干 = ${sortedSet1("葡萄干")}")
    println(s"是否有火龙果 = ${sortedSet1("火龙果")}")

    println("\n3: 使用+ 在TreeSet 中添加元素")
    val sortedSet2: SortedSet[String] = sortedSet1 + "火龙果" + "葡萄干"
    println(s"使用 + 向SortedSet集合中添加元素 = $sortedSet2")

    println("\n4: 使用 ++ 将两个Set集合添加到一起")
    val sortedSet3: SortedSet[String] = sortedSet1 ++ SortedSet[String]("火龙果", "葡萄干")
    println(s"使用 ++ 将两个SortedSet集合添加到一起 = $sortedSet3")

    println("\n5: 使用 - 从SortedSet中删除元素")
    val sortedSet4: SortedSet[String] = sortedSet1 - "苹果"
    println(s"去掉苹果后的SortedSet集合元素 = $sortedSet4")

    println("\n6: 使用& 找到两个集合之间的交集")
    val sortedSet5: SortedSet[String] = SortedSet("火龙果", "香蕉", "苹果")
    println(s"sortedSet1和sortedSet5的交集 = ${sortedSet1 & sortedSet5}")

    println("\n7: 使用 &~ 找出两个集合的差集")
    println(s"sortedSet1和sortedSet5的差集 = ${sortedSet1 &~ sortedSet5}")

    println("\n8: 改变SortedSet的顺序为降序字母排列")
    object AlphabetOrdering extends Ordering[String] {
      def compare(element1:String, element2:String): Int = element2.compareTo(element1)
    }
    val sortedSet6: SortedSet[String] = SortedSet("苹果","香蕉","葡萄干")(AlphabetOrdering)
    println(s"sortedSet6的元素 = $sortedSet6")

    println("\n9: 初始化一个空集")
    val emptySortedSet: SortedSet[String] = SortedSet.empty[String]
    println(s"Empty TreeSet = $emptySortedSet")
  }
}

即可保证顺序,又要保证唯一性,可以使用ListSet类型。

【示例】使用不可变的ListSet。

import scala.collection.immutable.ListSet

object ListSetDemo {
  def main(args: Array[String]): Unit = {
    println("1:初始化一个不可变的ListSet")
    // val listSet1: ListSet[String] = ListSet("苹果","香蕉","葡萄干")
    val listSet1 = ListSet("苹果","香蕉","葡萄干") // 使用类型推断
    println(s"listSet1的元素有 = $listSet1")

    println("\n2: 检查不可变ListSet的元素")
    println(s"Element 苹果 = ${listSet1("苹果")}")
    println(s"Element 香蕉 = ${listSet1("香蕉")}")
    println(s"Element 葡萄干 = ${listSet1("葡萄干")}")
    println(s"Element 草莓 = ${listSet1("草莓")}")

    println("\n3: ListSet是一个递归数据结构")
    println(s"head of listset is = ${listSet1.head}")
    println(s"tail of listset is = ${listSet1.tail}")
    println(s"tail of listset is = ${listSet1.tail.head}")
    println(s"tail of listset is = ${listSet1.tail.tail}")

    println("\n4: 使用+ 向不可变的ListSet添加元素")
    // val listSet2: ListSet[String] = listSet1 + "草莓"
    val listSet2: ListSet[String] = listSet1 + "葡萄干"
    println(s"在ListSet中添加元素,使用 + = $listSet2")

    println("\n5: 使用 ++ 将两个ListSet添加到一起")
    val listSet3: ListSet[String] = listSet1 ++ ListSet("火龙果","苹果")
    println(s"添加两个列表一起,使用 ++ = $listSet3")

    println("\n6: 从ListSet删除元素,使用 -")
    val listSet4: ListSet[String] = listSet1 - "苹果"
    println(s"没有苹果元素的ListSet = $listSet4")

    println("\n7: 初始化一个空的ListSet")
    val emptyListSet: ListSet[String] = ListSet.empty[String]
    println(s"String类型的空ListSet = $emptyListSet")
  }
}

什么是BitSet?

继之前的Set、HashSet、TreeSet和SortedSet之后,BitSet也允许存储没有可重复值的唯一元素。根据Scala文档,BitSet表示一个小整数的集合,作为一个大整数的位。在本例中,我们将重点关注BitSet的set语义。

【示例】学习如何使用Scala的不可变BitSet,并执行一些常见的操作,如初始化,添加元素,添加BitSet, BitSet差集和交集,以及创建空BitSet。

import scala.collection.immutable.BitSet

object BitSetDemo {
  def main(args: Array[String]): Unit = {
    println("1: 初始化一个具有3个元素的BitSet")
    val bitSet1: BitSet = BitSet(3, 2, 0)
    println(s"bitSet1中的元素 = $bitSet1")

    println("\n2: 在BitSet中检查特定的元素")
    println(s"Element 0 = ${bitSet1(0)}")
    println(s"Element 2 = ${bitSet1(2)}")
    println(s"Element 3 = ${bitSet1(3)}")
    println(s"Element 3 = ${bitSet1(1)}")

    println("\n3: 使用 + 在BitSet中添加元素")
    val bitSet2: BitSet = bitSet1 + 13 + 13
    println(s"使用 + 在BitSet中添加元素 = $bitSet2")

    println("\n4: 使用++ 将两个BitSet添加到一起")
    val bitSet3: BitSet = bitSet1 ++ BitSet(13, 14, 15, 16, 17)
    println(s"使用++ 将两个BitSet添加到一起 = $bitSet3")

    println("\n5: 使用- 在BitSet中删除元素")
    val bitSet4: BitSet = bitSet1 - 0
    println(s"移除元素0后的BitSet = $bitSet4")

    println("\n6: 使用& 找到两个BitSet之间的交集")
    val bitSet5: BitSet = BitSet(0, 2, 4)
    println(s"bitSet1和bitSet5的交集 = ${bitSet1 & bitSet5}")

    println("\n7:使用 &~ 找到两个BitSet之间的差集")
    println(s"bitSet1和bitSet5的差集 = ${bitSet1 &~ bitSet5}")

    println("\n8: 初始化一个空的 BitSet")
    val emptyBitSet: BitSet = BitSet.empty
    println(s"Empty BitSet = $emptyBitSet")
  }
}

《Spark原理深入与编程实战》