Scala—计算迭代器中每个键的出现次数

Scala—计算迭代器中每个键的出现次数,scala,iterator,Scala,Iterator,我有一个迭代器,其中包含一些键值对。 e、 g (詹,xyz)(肯,zxy)(詹,asd)(肯,asdf) 结果应该是 (简,2)(肯,2) 如何使用count函数(或任何其他函数)计算该特定集合的迭代器中每个键的出现次数 编辑: 在我的用例中,这个迭代器表示的集合有大量记录,可能在数百万条范围内,不,我需要最有效(时间复杂度较低)的方法来实现这一点。我发现默认的count方法是一种非常快速的方法,它可以以某种方式用于生成所需的结果。您可以groupBy键,然后计算每个键的出现次数: val i

我有一个迭代器,其中包含一些键值对。 e、 g

(詹,xyz)(肯,zxy)(詹,asd)(肯,asdf)

结果应该是

(简,2)(肯,2)

如何使用count函数(或任何其他函数)计算该特定集合的迭代器中每个键的出现次数

编辑:
在我的用例中,这个迭代器表示的集合有大量记录,可能在数百万条范围内,不,我需要最有效(时间复杂度较低)的方法来实现这一点。我发现默认的count方法是一种非常快速的方法,它可以以某种方式用于生成所需的结果。

您可以
groupBy
键,然后计算每个键的出现次数:

val iterator = 
  Iterator(("jen","xyz"), ("ken","zxy"), ("jen","asd"), ("ken", "asdf"))

iterator.toList.groupBy(_._1).mapValues(_.length).toList
// List[(String, Int)] = List((jen,2), (ken,2))

您可以
groupBy
键,然后计算每个键的出现次数:

val iterator = 
  Iterator(("jen","xyz"), ("ken","zxy"), ("jen","asd"), ("ken", "asdf"))

iterator.toList.groupBy(_._1).mapValues(_.length).toList
// List[(String, Int)] = List((jen,2), (ken,2))

Peter Neyens建议的方法是可行的,但由于使用了
toList
groupBy
length
的方式,对于某些应用程序来说,它可能非常低效(时间和内存)。通常,将计数直接聚合到一个映射中并避免所有不必要的
列表的创建将更加有效

import scala.collection.TraversableOnce
import scala.collection.mutable.HashMap

def counts[T](xs: TraversableOnce[T]): Map[T, Int] = {
  xs.foldLeft(HashMap.empty[T, Int].withDefaultValue(0))((acc, x) => { acc(x) += 1; acc}).toMap
}
定义了
counts
方法后,可以将其应用于键值对迭代器,如下所示:

val iter: Iterator[(String, String)] = ???
val keyCounts = counts(iter.map(_._1))
上面定义的
计数
方法适用于大量值的
迭代器
,例如

val iter = Iterator.range(0, 100000000).map(i => (i % 1931, i))
val countMap = counts(iter.map(_._1))
// Map(645 -> 51787, 892 -> 51787, 69 -> 51787, 1322 -> 51786, ...)
效果很好,而Peter的回答中建议的方法,即

val iter = Iterator.range(0, 100000000).map(i => (i % 1931, i))
val countMap = iter.toList.groupBy(_._1).mapValues(_.length).toMap

持续一段时间,最终导致
OutOfMemoryError
。它失败的原因是由于所有不必要的
列表创建。

Peter Neyens建议的方法会起作用,但由于使用
toList
groupBy
length
的方式,对于某些应用程序来说,它可能非常低效(时间和内存)。通常,将计数直接聚合到一个映射中并避免所有不必要的
列表的创建将更加有效

import scala.collection.TraversableOnce
import scala.collection.mutable.HashMap

def counts[T](xs: TraversableOnce[T]): Map[T, Int] = {
  xs.foldLeft(HashMap.empty[T, Int].withDefaultValue(0))((acc, x) => { acc(x) += 1; acc}).toMap
}
定义了
counts
方法后,可以将其应用于键值对迭代器,如下所示:

val iter: Iterator[(String, String)] = ???
val keyCounts = counts(iter.map(_._1))
上面定义的
计数
方法适用于大量值的
迭代器
,例如

val iter = Iterator.range(0, 100000000).map(i => (i % 1931, i))
val countMap = counts(iter.map(_._1))
// Map(645 -> 51787, 892 -> 51787, 69 -> 51787, 1322 -> 51786, ...)
效果很好,而Peter的回答中建议的方法,即

val iter = Iterator.range(0, 100000000).map(i => (i % 1931, i))
val countMap = iter.toList.groupBy(_._1).mapValues(_.length).toMap

持续一段时间,最终导致
OutOfMemoryError
。它失败的原因是因为所有不必要的
列表创建。

Peter,请参见下面我的答案。使用
groupBy
获取列表,然后按
length
对每个列表进行计数将是非常低效的。@JasonLenderman,“非常低效”?这几乎肯定不会对绝大多数案件产生影响。微优化只是愚蠢的,直到你真的需要它们(这是非常罕见的)。另外,如果你真的很关心那一点点的效率,你可能应该重写你的答案,不要使用
foldLeft
;毕竟,
虽然
循环总是会更有效率…@dhg,
groupBy
方法是为每个键创建一个新的
列表
,并且这些
列表中的每一个都在内存中实例化。此外,还需要对每个列表进行额外的遍历来计算长度。如果正在迭代的不同键的数量(以及它们出现的次数)很小,那么这可能不是一个大问题,但对于某些应用程序来说可能真的很糟糕。至于使用while循环,我没有这样做,因为我认为这是一个微观优化。为什么?因为无论应用程序或数据如何,它只会导致不断的因子改进。我同意了解中间集合很重要。毕竟,有多少人的名字叫肯?我认识几个詹,但那一刻可能已经过去了。@JasonLenderman不,我认为OP不关心xyz,只关心你的计数。OP的下一篇文章将用给定长度的值来计算键数。使用
groupBy
获取列表,然后按
length
对每个列表进行计数将是非常低效的。@JasonLenderman,“非常低效”?这几乎肯定不会对绝大多数案件产生影响。微优化只是愚蠢的,直到你真的需要它们(这是非常罕见的)。另外,如果你真的很关心那一点点的效率,你可能应该重写你的答案,不要使用
foldLeft
;毕竟,
虽然
循环总是会更有效率…@dhg,
groupBy
方法是为每个键创建一个新的
列表
,并且这些
列表中的每一个都在内存中实例化。此外,还需要对每个列表进行额外的遍历来计算长度。如果正在迭代的不同键的数量(以及它们出现的次数)很小,那么这可能不是一个大问题,但对于某些应用程序来说可能真的很糟糕。至于使用while循环,我没有这样做,因为我认为这是一个微观优化。为什么?因为无论应用程序或数据如何,它只会导致不断的因子改进。我同意了解中间集合很重要。毕竟,有多少人的名字叫肯?我认识几个詹,但那一刻可能已经过去了。@JasonLenderman不,我认为OP不关心xyz,只关心你的计数。OP的下一篇文章将用给定长度的值来计数键。嘿,不必要的映射怎么了?一个扩展方法如何
countBy(f:A=>K)
?我的理解是OP对值不感兴趣,他只对计算每个键遇到的次数感兴趣。所以m