Problem

你正在使用一个巨大的集合,并且想创建一个懒加载的版本。只有在计算或者返回结果时才真正被调用。

Solution

除了Stream类,不论什么时候你创建一个Scala集合类的实例,你都创建了一个strict版本集合(任何操作都会被立即执行)。这意味着如果你新建了一个百万元素的集合,这些元素会立即加载进内存。在Java中这是正常的,但是在Scala中你可以选择在集合上新建一个视图。视图可以让结果nonstrict,或者懒加载。这改变了结果集合,所以当调用集合的转换方法的时候,只有真正要访问集合元素的时候才会执行计算,并且不像平时那样是“立即执行”。(转换方法是把一个输入集合转化为一个输出集合。)

你可以看下创建集合的时候使用view与不使用view的区别:

scala> val nums = 1 to 100
nums: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)scala> val numsView = (1 to 100).view
numsView: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...)

不使用view创建一个Range就像你期望的结果一样,一个100个元素的Range。然而,使用view的Range在REPL中出现了不同的输出结果,一个叫做SeqView的东西。

这个SeqView带有如下信息:

  • 集合元素类型为Int

  • 输出结果scala.collection.immutable.IndexedSeq[Int],暗示了你使用force方法把view转回正常集合时候你能得到的集合元素类型。

你会看到下面的信息,如果你强制把一个view转回一个普通集合:

scala> val numsView = (1 to 100).view
numsView: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...)scala> val x = numsView.force
x: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)

存在许多中方法能看到使用一个集合view的效果。首先,我们来看一看foreach方法,它好像没什么区别。

scala> (1 to 100).foreach(x => print(x + " "))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
scala> (1 to 100).view.foreach(x => print(x + " "))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

这两个例子都会直接打印出集合的100个元素,因为foreach方法并不是一个转换方法,所以对结果没有影响。

但是当你调用一个转换方法的时候,你会戏剧性的发现结果变得不同了:

scala> (1 to 10).map(_ * 2)
res61: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)scala> (1 to 10).view.map(_ * 2)
res62: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)

结果不同了,应为map是一个转换方法。我们来使用下面的代码来更深层次的展示一下这种不同:

scala> (1 to 10).map{x => {|   Thread.sleep(1000)|   x * 2| }}
res68: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)scala> (1 to 10).view.map{x => {|   Thread.sleep(1000)|   x * 2| }}
res69: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)

不是用view的时候,程序会等待10秒,然后直接返回结果。使用view,程序则直接返回scala.collection.SeqView。

Discussion

Scala文档对view做出了一个说明:“仅仅对集合的结果构造了代理,它的元素构件只有一个要求...一个view是一个特殊类型的集合,它实现了集合的一些基本方法,但是对所有的transformers实现了懒加载”

一个transformer方法是能够从一个原有集合构造一个新的集合。这样的方法包括map,filter,reverse等等。当你使用这些方法的时候,你就在把一个输入集合转化为一个输出集合。

这就解释了为什么foreach方法在使用view和没有使用view时没有任何区别:它不是一个transformer方法。但是map方法和其他transformer方法比如reverse,就可以有懒加载的效果:

scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)scala> l.view.reverse
res70: scala.collection.SeqView[Int,List[Int]] = SeqViewR(...)
Use cases

对于view,有两个主要的使用场景:

  • 性能

  • 像处理数据库视图一样处理集合

关于性能,驾驶你遇到一种情况,不得不处理一个十亿元素的集合。如果你不得不做的话,你肯定不希望直接在10亿元素上运行一个算法,所以这时候使用一个视图是有意义的。

第二个应用场景让你使用Scala view就像使用一个数据库view一样。下面这段代码展示了如何把一个scala集合view当作一个数据库view使用:

scala> val arr = (1 to 10).toArray
arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)scala> val view = arr.view.slice(2, 5)
view: scala.collection.mutable.IndexedSeqView[Int,Array[Int]] = SeqViewS(...)scala> arr(2) = 42scala> view.foreach(println)
42
4
5scala> view(0) = 10scala> view(1) = 20scala> view(2) = 30scala> arr
res76: Array[Int] = Array(1, 2, 10, 20, 30, 6, 7, 8, 9, 10)

改变数组中元素的值会改变view,改变view中对应数据元素的值同样会改变数组元素值。当你想要修改一个集合子集的元素时,给集合创建一个view然后修改对应的元素是一个非常好的方法来实现这个目标。

最后需要注意的是,不要错误的认为使用view可以节省内存。下面这两个行为会抛出一个“java.lang.OutOfMemoryError:Java heap space”错误信息:

scala> val a = Array.range(0,123456789)
java.lang.OutOfMemoryError: Java heap spacescala> val a = Array.range(0,123456789).view
java.lang.OutOfMemoryError: Java heap space

最后说一句,视图就是推迟执行,该用多大内存还使用多大内存,该遍历多少元素还是遍历多少元素。说白了scala视图就跟数据库视图一样,不使用视图就跟数据库建立临时表一样。使用视图,当原始集合改变的时候,不需要重新跑transformers方法,使用视图则每次使用视图的时候都会跑一次transformers方法内容。

scala> def compare(x: Int): Boolean = {|   println(s"compare $x and 5")|   return x < 5| }
compare: (x: Int)Booleanscala> val l = List(1,2,3,4,5,6,7,8,9).view.filter(x => compare(x))
l: scala.collection.SeqView[Int,List[Int]] = SeqViewF(...)scala> l.map(_ * 2)
res80: scala.collection.SeqView[Int,Seq[_]] = SeqViewFM(...)scala> l.map(_ * 2).force
compare 1 and 5
compare 2 and 5
compare 3 and 5
compare 4 and 5
compare 5 and 5
compare 6 and 5
compare 7 and 5
compare 8 and 5
compare 9 and 5
res82: Seq[Int] = List(2, 4, 6, 8)

scala中给集合创建懒加载view视图相关推荐

  1. 在 Swift 中使用闭包实现懒加载

    本文讲的是在 Swift 中使用闭包实现懒加载, 原文地址:Swift Lazy Initialization with Closures 原文作者:Bob Lee 译文出自:掘金翻译计划 译者:ls ...

  2. fetchtype 动态控制_hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    对这种懒加载问题,最后的做法是利用Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,使得Hi ...

  3. java懒加载注解_在springboot中实现个别bean懒加载的操作

    懒加载---就是我们在spring容器启动的是先不把所有的bean都加载到spring的容器中去,而是在当需要用的时候,才把这个对象实例化到容器中. @Lazy 在需要懒加载的bean上加上@Lazy ...

  4. [译]带你揭开Kotlin中属性代理和懒加载语法糖衣

    翻译说明: 原标题: How Kotlin's delegated properties and lazy-initialization work 原文地址: https://medium.com/t ...

  5. element中树形数据与懒加载实现全部展开和全部收起

    element中属性懒加载数据 default-expand-all属性::是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效 如果在表格头上加上一个按钮实现全部展开与收起 类似如 ...

  6. hibernate中延迟加载的设定(懒加载的设定)

    User.hbm.xml: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernat ...

  7. mui ajax 懒加载,MUI框架运用中遇见问题总结

    H5在移动端的开发趋势化越来越大,相对App来说,H5优势有: 跨平台,兼容性强 开发速度快,成本较低 迭代周期短 技术成本低 但当我们在开始移动端的项目开发时,又愁着有什么样的好的UI框架能让我们减 ...

  8. vue-photo-preview踩坑,el-table中一张错误图片导致全部图片无法放大,并且与懒加载v-lazy不兼容

    需求:el-table中展示的图片,可以点击放大,并且使用懒加载(偶尔会有404的图片路径) 坑一: 一张404图片会导致全部图片无法放大,用@error解决 坑二: 但是马上发现如果用了vue-la ...

  9. 前端性能优化总结/懒加载、函数节流、优化dom操作、雪碧图、合并文件

    1.减少 HTTP 请求数量 在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信.浏览器与服务器需要经过三次握手,每次握手需要花费大量时间.而且不同浏览器对资源文件并发请求数量有限(不同浏览器 ...

最新文章

  1. 遮挡也能识别?地平线提出时序信息提升行人检测准确度|​CVPR 2020
  2. 深度学习分布式策略优化、显存优化、通信优化、编译优化综述
  3. apache apollo php,php windows環境 安裝 Apache-apollo + phpMQTT 實現發送 MQTT
  4. 支付宝服务窗的简单开发体会
  5. 凤凰涅槃:从 iBatis 到 MyBatis
  6. python 仪表盘_如何使用Python刮除仪表板
  7. mysql数据库(4): 创建并选择数据库
  8. 代码简洁(注意事项)
  9. 牛的旅行(信息学奥赛一本通-T1343)
  10. xp 优化   转自天涯
  11. python命令提示符后的小白块是什么_Python:响应命令行提示
  12. 4.测试用例模板(p2p)
  13. 慕课软件质量保证与测试(第九章.课后作业)
  14. 发微博利器 FaWave(发微)---- chrome扩展程序
  15. 已解决json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  16. 江苏科技大学计算机学院院长高尚,江苏科技大学计算机科学与工程学院导师介绍...
  17. 计算机增加一个硬盘怎么设置方法,电脑加硬盘【操作教程】
  18. 国内两家3D打印企业入选微软加速器创业加速计划
  19. gitweb 搭建教程
  20. python中字符串的使用04字符串大小写转换、删除空白字符

热门文章

  1. 项目在云服务器上的绝对路径,服务器上的绝对路径怎么写
  2. java 字符长度 中文_java判断中文字符串长度的简单实例
  3. ERROR: No query specified
  4. NPAPI和PPAPI开发
  5. java快速教程_Java快速入门
  6. oracle11g 查看磁盘,oracle11g 磁盘
  7. arg是什么函数_怎么实现边听歌边搜图?线程初体验:常用函数
  8. 跑python gpu利用率低_训练效率低?GPU利用率上不去?快来看看别人家的tricks吧~...
  9. pypark hive 开启动态分区_Hive分区与分桶
  10. docker swarm MySQL_容器与云|在 Docker 中运行 MySQL:多主机网络下 Docker Swarm 模式的容器管理...