1.大数据笔试真题集锦---第十七章Scala面试题
2.SPARK-38864 - Spark支持unpivot源码分析
3.sparkåhadoopåªä¸ªå¥½
4.如何证明,隐隐式在n个元素组成的式转使用集合中取n+1个不同的三元子集,求证必有两个子集,换源它们恰有一个公共元?
大数据笔试真题集锦---第十七章Scala面试题
我会持续更新维护,转换希望能为寻找大数据工作的隐隐式朋友们提供帮助。
第十七章 Scala
Scala 是式转使用燕赵河北麻将源码一种多范式编程语言,融合了多种语言的换源优点,并运行在Java虚拟机上,转换使得代码与Java丰富的隐隐式类库可以无缝对接。Scala既支持面向对象编程,式转使用也支持函数式编程,换源其代码简洁明了,转换同时具备静态语言的隐隐式严谨性,相比于Java,式转使用Scala的换源代码更精简,功能更强大。
Scala可用于Spark开发,云圈源码其简洁的代码编写方式和高效的执行效率使其在大数据处理领域大放异彩,相较于Java,Scala代码量减少数倍,更加灵活。
在Scala中,懒加载可通过使用`Lazy`关键字实现,以避免不必要的计算。
Scala没有`break`操作,但可通过`Breaks`对象实现类似Java的跳出语句功能。`case class`代表样例类,它提供了一种无需序列化的快捷方式,用于传递参数,如在DataSet操作中转换RDD或DataFrame时。
元组在Scala中用于封装多个值,隐式转换则是一种自动将值从一种类型转换为另一种类型的函数。隐式转换在Scala中主要应用于类型调用,传奇架设源码或在方法内作为局部变量,以便于直接调用。
“闭包”指一个函数封装了外部环境中的局部变量,使其作为全局变量使用。
Scala的`Option`类型表示可能有的值或无值,通过`Some`和`None`表示,常见于Map集合取值操作。
`偏函数`涉及使用`case`进行类型匹配的函数,常用于匹配特定属性值,在Spark算子中常见。
单例模式是一种设计模式,确保系统中只有一个实例。在Scala中,通过伴生对象与伴生类的组合实现。
柯里化是一种将接受多个参数的函数转换为一系列单参数函数的过程。
Scala的老板皇冠源码模式匹配功能强大,包括一系列备选项,每个匹配项对应一个模式或表达式,而Java的匹配模式仅适用于特定类型,且需使用`break`跳出。
尾递归是优化递归调用的方法,避免栈溢出错误。
Scala中的`Unit`类似于Java中的`void`,表示无返回值。
`to`和`until`的区别在于`to`包含头尾元素,而`until`不包含尾元素。
`var`、`val`和`def`关键字分别用于变量声明、常量声明和方法定义。
`trait`(特质)和`abstract class`(抽象类)之间的区别在于继承、构造函数和方法定义。
`unapply`和`apply`方法用于提取器的跑腿源码模块创建,分别用于从对象中提取参数和构建对象。
Scala类型系统中的`Nil`、`Null`、`None`、`Nothing`分别代表空列表、空引用、无值和不存在类型。
`call-by-value`和`call-by-name`求值策略的区别在于计算时间点。
`yield`用于生成新值,是`comprehension`语法的组成部分,实现多个操作的组合。
高阶函数是指能接受或返回其他函数的函数,如`filter`、`map`、`flatMap`等。
使用Scala全排序过滤字段时,确保满足特定条件,如在1到4的序列中,避免2位于第一位,且3和4不相邻。
SPARK- - Spark支持unpivot源码分析
unpivot是数据库系统中用于列转行的内置函数,如SQL SERVER, Oracle等。以数据集tb1为例,每个数字代表某个人在某个学科的成绩。若要将此表扩展为三元组,可使用union实现。但随列数增加,SQL语句变长。许多SQL引擎提供内置函数unpivot简化此过程。unpivot使用时需指定保留列、进行转行的列、新列名及值列名。
SPARK从SPARK-版本开始支持DataSet的unpivot函数,逐步扩展至pyspark与SQL。在Dataset API中,ids为要保留的Column数组,Column类提供了从String构造Column的隐式转换,方便使用。利用此API,可通过unpivot函数将数据集转换为所需的三元组。values表示转行列,variableColumnName为新列名,valueColumnName为值列名。
Analyser阶段解析unpivot算子,将逻辑执行计划转化为物理执行计划。当用户开启hive catalog,SPARK SQL根据表名和metastore URL查找表元数据,转化为Hive相关逻辑执行计划。物理执行计划如BroadcastHashJoinExec,表示具体的执行策略。规则ResolveUnpivot将包含unpivot的算子转换为Expand算子,在物理执行计划阶段执行。此转换由开发者自定义规则完成,通过遍历逻辑执行计划树,根据节点类型及状态进行不同处理。
unpivot函数实现过程中,首先将原始数据集投影为包含ids、variableColumnName、valueColumnName的列,实现语义转换。随后,通过map函数处理values列,构建新的行数据,最终返回Expand算子。在物理执行计划阶段,Expand算子将数据转换为所需形式,实现unpivot功能。
综上所述,SPARK内置函数unpivot的实现通过解析列参数,组装Expand算子完成,为用户提供简便的列转行功能。通过理解此过程,可深入掌握SPARK SQL的开发原理与内在机制。
sparkåhadoopåªä¸ªå¥½
大æ°æ®ç浪潮é£é¡å ¨ççæ¶åï¼Sparkç«äºãå¨å½å¤ Yahoo!ãTwitterãIntelãAmazonãCloudera çå ¬å¸çå åºç¨å¹¶æ¨å¹¿ Spark ææ¯ï¼å¨å½å é¿éå·´å·´ãç¾åº¦ãæ·å®ãè ¾è®¯ãç½æãæç¯çå ¬å¸æ¢ä¸ºäººå ï¼å¹¶ä¹äºå享ãå¨éåçåå±ä¸ï¼IBMãHortonworksãå¾®çç¥çå ¬å¸çº·çº·å° Spark èè¿ç°æ解å³æ¹æ¡ï¼å¹¶å å ¥ Spark éµè¥ãSpark å¨ITä¸ççåºç¨å¯è°æç«çåä¹å¿ã
åæ°é½æ¯ç«å¨å·¨äººçè©èä¸äº§ççï¼å¨å¤§æ°æ®é¢åSparkä¹ä¸ä¾å¤ãå¨ Spark åºç°åï¼è¦å¨ä¸ä¸ªå¹³å°å åæ¶å®ææ¹å¤çãåç§æºå¨å¦ä¹ ãæµå¼è®¡ç®ãå¾è®¡ç®ãSQL æ¥è¯¢çæ°ç§å¤§æ°æ®åæä»»å¡ï¼å°±ä¸å¾ä¸ä¸å¤å¥ç¬ç«çç³»ç»æ交éï¼è¿éè¦ç³»ç»é´è¿è¡ä»£ä»·è¾å¤§çæ°æ®è½¬å¨ï¼ä½æ¯è¿æ çä¼å¢å è¿ç»´è´æ ãSparkä¸å¼å§å°±çåäºæ§è½ï¼å®ç°äºå¨å åä¸è®¡ç®ã è¯é¢è®¨è®ºï¼1.Spark为å¥è¿ä¹ç«ï¼Sparkæ¡æ¶éç¨çç¼ç¨è¯è¨æ¯ä»ä¹ï¼æ¯å¦å®¹æä¸æï¼2. Sparkè½å¦æ为Hadoopçæ¿ä»£è å¢ï¼ä¸ºä»ä¹ï¼å®ä»¬æåªäºç¸ä¼¼ç¹ä¸åºå«ï¼3.ä½ä¸ºä¸ç§å åçè¿ä»£è®¡ç®æ¡æ¶ï¼Spark使ç¨åªäºåºæ¯ï¼4.æ·å®ä¸ºä»ä¹ä¼éæ©Spark计ç®æ¡æ¶å¢ï¼5.Mesos æ¯ä¸ä¸ªè½å¤è®©å¤ä¸ªåå¸å¼åºç¨åæ¡æ¶è¿è¡å¨åä¸é群ä¸çé群管çå¹³å°ãé£ä¹å®æ¯å¦ä½æ¥è°åº¦åè¿è¡Sparkçå¢ï¼6.Spark 为ä»ä¹ä¼éæ©å¼¹æ§åå¸å¼æ°æ®éï¼RDDï¼ä½ä¸ºå®çæ°æ®åå¨æ ¸å¿ï¼èä¸æ¯åå¸å¼å ±äº«å åï¼Distributed Shared Memoryï¼DSMï¼å®ä»¬æåªäºåºå«ï¼ 7.Spark on YARNä¸Sparkæå¥åºå«ï¼8.æ人è§å¾ï¼å¤§æ°æ®æ¶ä»£ï¼æç²¾é«çITææ¯æ¯Hadoop ,Yarn,Sparkï¼æ¨æ¯å¦ä½éªè¿ï¼ç好åªä¸ªï¼
1.Spark为å¥è¿ä¹ç«ï¼Sparkæ¡æ¶éç¨çç¼ç¨è¯è¨æ¯ä»ä¹ï¼æ¯å¦å®¹æä¸æï¼
Sparkæ¯åºäºå åçè¿ä»£è®¡ç®æ¡æ¶ï¼éç¨äºéè¦å¤æ¬¡æä½ç¹å®æ°æ®éçåºç¨åºåï¼å¦pageRankãK-Meansçç®æ³å°±é常éåå åè¿ä»£è®¡ç®ãSparkæ´ä¸ªçæä½ç³»æ£éæ¸å®åä¸ï¼GraphX ã SparkSQLã SparkStreaming ã MLlibï¼çå°Sparkæäºèªå·±çæ°æ®ä»åºåï¼é£å°±å®å ¨è½ä¸Hadoopçæä½ç³»ç¸åª²ç¾ã Sparkæ¡æ¶éç¨å½æ°å¼ç¼ç¨è¯è¨Scalaï¼Scalaè¯è¨çé¢å对象ãå½æ°å¼ãé«å¹¶å模åçç¹ç¹ï¼ä½¿å¾Sparkæ¥æäºæ´é«ççµæ´»æ§åæ§è½ãå¦æä½ å¦è¿javaï¼å¯è½ä¼å¯¹scalaä¸çä¸äºæ°æ¦å¿µè¡¨ç¤ºéçï¼å¦éå¼è½¬æ¢ã模å¼å¹é ãä¼´çç±»çï¼ä½ä¸æ¦å ¥é¨ï¼ä½ ä¼æè§scalaè¯è¨çç®æ´ä¸å¼ºå¤§ã
2. Sparkè½å¦æ为Hadoopçæ¿ä»£è å¢ï¼ä¸ºä»ä¹ï¼å®ä»¬æåªäºç¸ä¼¼ç¹ä¸åºå«ï¼
两è ç侧éç¹ä¸åï¼ä½¿ç¨åºæ¯ä¸åï¼ä¸ªäººè®¤ä¸ºæ²¡ææ¿ä»£ä¹è¯´ãSparkæ´éåäºè¿ä»£è¿ç®æ¯è¾å¤çMLåDMè¿ç®ãå 为å¨Sparkéé¢ï¼æRDDçæ¦å¿µãRDDå¯ä»¥cacheå°å åä¸ï¼é£ä¹æ¯æ¬¡å¯¹RDDæ°æ®éçæä½ä¹åçç»æï¼é½å¯ä»¥åæ¾å°å åä¸ï¼ä¸ä¸ä¸ªæä½å¯ä»¥ç´æ¥ä»å åä¸è¾å ¥ï¼çå»äºMapReduce大éçç£çIOæä½ãä½æ¯ï¼æ们ä¹è¦çå°sparkçéå¶ï¼å åãæ认为Hadoopè½ç¶è´¹æ¶ï¼ä½æ¯å¨OLAPç大è§æ¨¡æ°æ®çåºç¨åºæ¯ï¼è¿æ¯å欢è¿çãç®åHadoop涵çäºä»æ°æ®æ¶éãå°åå¸å¼åå¨ï¼åå°åå¸å¼è®¡ç®çå个é¢åï¼å¨åé¢åé½æèªå·±ç¬ç¹ä¼å¿ã
3. ä½ä¸ºä¸ç§å åçè¿ä»£è®¡ç®æ¡æ¶ï¼Sparkéç¨åªäºåºæ¯ï¼
éç¨äºè¿ä»£æ¬¡æ°æ¯è¾å¤çåºæ¯ãè¿ä»£æ¬¡æ°å¤çæºå¨å¦ä¹ ç®æ³çãå¦pageRankãK-Meansçã
4. æ·å®ä¸ºä»ä¹ä¼éæ©Spark计ç®æ¡æ¶å¢ï¼
è¿ä¸»è¦åºäºæ·å®ä¸å¡çåºç¨åºæ¯ï¼å ¶æ¶åäºå¤§è§æ¨¡çæ°æ®å¤çä¸åæãå ¶ä¸»è¦æ¯åºç¨SparkçGraphXå¾è®¡ç®ï¼ä»¥ä¾¿è¿è¡ç¨æ·å¾è®¡ç®ï¼åºäºæ大è¿éå¾ç社åºåç°ãåºäºä¸è§å½¢è®¡æ°çå ³ç³»è¡¡éãåºäºéæºæ¸¸èµ°çç¨æ·å±æ§ä¼ æçã
5.Mesos æ¯ä¸ä¸ªè½å¤è®©å¤ä¸ªåå¸å¼åºç¨åæ¡æ¶è¿è¡å¨åä¸é群ä¸çé群管çå¹³å°ãé£ä¹å®æ¯å¦ä½æ¥è°åº¦åè¿è¡Sparkçå¢ï¼
ç®åå¨Spark On Mesosç¯å¢ä¸ï¼ç¨æ·å¯éæ©ä¸¤ç§è°åº¦æ¨¡å¼ä¹ä¸è¿è¡èªå·±çåºç¨ç¨åº ç²ç²åº¦æ¨¡å¼ï¼Coarse-grained Modeï¼ï¼æ¯ä¸ªåºç¨ç¨åºçè¿è¡ç¯å¢ç±ä¸ä¸ªDirveråè¥å¹²ä¸ªExecutorç»æï¼å ¶ä¸ï¼æ¯ä¸ªExecutorå ç¨è¥å¹²èµæºï¼å é¨å¯è¿è¡å¤ä¸ªTaskï¼å¯¹åºå¤å°ä¸ªâslotâï¼ãåºç¨ç¨åºçå个任å¡æ£å¼è¿è¡ä¹åï¼éè¦å°è¿è¡ç¯å¢ä¸çèµæºå ¨é¨ç³è¯·å¥½ï¼ä¸è¿è¡è¿ç¨ä¸è¦ä¸ç´å ç¨è¿äºèµæºï¼å³ä½¿ä¸ç¨ï¼æåç¨åºè¿è¡ç»æåï¼åæ¶è¿äºèµæºã举个ä¾åï¼æ¯å¦ä½ æ交åºç¨ç¨åºæ¶ï¼æå®ä½¿ç¨5个executorè¿è¡ä½ çåºç¨ç¨åºï¼æ¯ä¸ªexecutorå ç¨5GBå åå5个CPUï¼æ¯ä¸ªexecutorå é¨è®¾ç½®äº5个slotï¼åMesoséè¦å 为executoråé èµæºå¹¶å¯å¨å®ä»¬ï¼ä¹åå¼å§è°åº¦ä»»å¡ãå¦å¤ï¼å¨ç¨åºè¿è¡è¿ç¨ä¸ï¼mesosçmasteråslave并ä¸ç¥éexecutorå é¨å个taskçè¿è¡æ åµï¼executorç´æ¥å°ä»»å¡ç¶æéè¿å é¨çéä¿¡æºå¶æ±æ¥ç»Driverï¼ä»ä¸å®ç¨åº¦ä¸å¯ä»¥è®¤ä¸ºï¼æ¯ä¸ªåºç¨ç¨åºå©ç¨mesosæ建äºä¸ä¸ªèæé群èªå·±ä½¿ç¨ã ç»ç²åº¦æ¨¡å¼ï¼Fine-grained Modeï¼ï¼é´äºç²ç²åº¦æ¨¡å¼ä¼é æ大éèµæºæµªè´¹ï¼Spark On Mesosè¿æä¾äºå¦å¤ä¸ç§è°åº¦æ¨¡å¼ï¼ç»ç²åº¦æ¨¡å¼ï¼è¿ç§æ¨¡å¼ç±»ä¼¼äºç°å¨çäºè®¡ç®ï¼ææ³æ¯æéåé ãä¸ç²ç²åº¦æ¨¡å¼ä¸æ ·ï¼åºç¨ç¨åºå¯å¨æ¶ï¼å ä¼å¯å¨executorï¼ä½æ¯ä¸ªexecutorå ç¨èµæºä» ä» æ¯èªå·±è¿è¡æéçèµæºï¼ä¸éè¦èèå°æ¥è¦è¿è¡çä»»å¡ï¼ä¹åï¼mesosä¼ä¸ºæ¯ä¸ªexecutorå¨æåé èµæºï¼æ¯åé ä¸äºï¼ä¾¿å¯ä»¥è¿è¡ä¸ä¸ªæ°ä»»å¡ï¼å个Taskè¿è¡å®ä¹åå¯ä»¥é©¬ä¸éæ¾å¯¹åºçèµæºãæ¯ä¸ªTaskä¼æ±æ¥ç¶æç»Mesos slaveåMesos Masterï¼ä¾¿äºæ´å ç»ç²åº¦ç®¡çå容éï¼è¿ç§è°åº¦æ¨¡å¼ç±»ä¼¼äºMapReduceè°åº¦æ¨¡å¼ï¼æ¯ä¸ªTaskå®å ¨ç¬ç«ï¼ä¼ç¹æ¯ä¾¿äºèµæºæ§å¶åé离ï¼ä½ç¼ºç¹ä¹å¾ææ¾ï¼çä½ä¸è¿è¡å»¶è¿å¤§ã
6.Spark 为ä»ä¹ä¼éæ©å¼¹æ§åå¸å¼æ°æ®éï¼RDDï¼ä½ä¸ºå®çæ°æ®åå¨æ ¸å¿ï¼èä¸æ¯åå¸å¼å ±äº«å åï¼Distributed Shared Memoryï¼DSMï¼å®ä»¬æåªäºåºå«ï¼
RDDæ¯Sparkçæåºæ¬æ½è±¡,æ¯å¯¹åå¸å¼å åçæ½è±¡ä½¿ç¨ï¼å®ç°äºä»¥æä½æ¬å°éåçæ¹å¼æ¥æä½åå¸å¼æ°æ®éçæ½è±¡å®ç°ãRDDæ¯Sparkææ ¸å¿çä¸è¥¿ï¼å®è¡¨ç¤ºå·²è¢«ååºï¼ä¸å¯åç并è½å¤è¢«å¹¶è¡æä½çæ°æ®éåï¼ä¸åçæ°æ®éæ ¼å¼å¯¹åºä¸åçRDDå®ç°ãRDDå¿ é¡»æ¯å¯åºååçãRDDå¯ä»¥cacheå°å åä¸ï¼æ¯æ¬¡å¯¹RDDæ°æ®éçæä½ä¹åçç»æï¼é½å¯ä»¥åæ¾å°å åä¸ï¼ä¸ä¸ä¸ªæä½å¯ä»¥ç´æ¥ä»å åä¸è¾å ¥ï¼çå»äºMapReduce大éçç£çIOæä½ãè¿å¯¹äºè¿ä»£è¿ç®æ¯è¾å¸¸è§çæºå¨å¦ä¹ ç®æ³, 交äºå¼æ°æ®æææ¥è¯´ï¼æçæåæ¯è¾å¤§ã RDDåªè½ä»æä¹ åå¨æéè¿Transformationsæä½äº§çï¼ç¸æ¯äºåå¸å¼å ±äº«å åï¼DSMï¼å¯ä»¥æ´é«æå®ç°å®¹éï¼å¯¹äºä¸¢å¤±é¨åæ°æ®ååºåªéæ ¹æ®å®çlineageå°±å¯éæ°è®¡ç®åºæ¥ï¼èä¸éè¦åç¹å®çCheckpointã
7.Spark on YARNä¸Sparkæå¥åºå«ï¼
让Sparkè¿è¡å¨ä¸ä¸ªéç¨çèµæºç®¡çç³»ç»ï¼å¦yarnï¼ä¹ä¸ï¼æ大ç好å¤æ¯éä½è¿ç»´ææ¬åæé«èµæºå©ç¨çï¼èµæºæéåé ï¼ï¼é¨å容éæ§åèµæºç®¡ç交ç±ç»ä¸çèµæºç®¡çç³»ç»å®æãèsparkåç¬æ¯æ æ³æææé«èµæºå©ç¨çã
8.æ人è§å¾ï¼å¤§æ°æ®æ¶ä»£ï¼æç²¾é«çITææ¯æ¯Hadoop ,Yarn,Sparkï¼æ¨æ¯å¦ä½éªè¿ï¼ç好åªä¸ªï¼
Yarnä¸å°±æ¯Hadoop MapReduceæ°æ¡æ¶åï¼è¿é为ä½è¦åç¬ååºã个人认为å½ä¸Hadoopçæä½ç³»ç¸å½åºå¤§ï¼ä¸ææ¯æ¥è¶æçï¼Sparkè¿æå¾ åå±ãå¦ææä¸å¤©ï¼Hadoopå è¿å å计ç®æ¨¡åï¼å°æ¶ï¼Sparkåä¼æ¯ææ ·çå¤å¢å¢ï¼
如何证明,在n个元素组成的集合中取n+1个不同的三元子集,求证必有两个子集,它们恰有一个公共元?
前言本文目录
Scala字符串 Scala 集合 1. 数组 2. list 3. set 4. Map 4.2 map遍历 方式一: foreach 方式二: 迭代器 方式三: for循环 5. 元组 trait特性 模式匹配match-case 并发 Actor Model Actor的特征: Actor与Actor之间通信: Scala隐式转换系统 隐式值 隐式视图 隐式类
Scala字符串
Scala中字符串也是分为两种: 可变长度的StringBuilder和不可变长度的String, 其操作用法与Java几乎一致.
接下来, 通过代码来查看常用方法:
//定义字符串 val str1 = "Hello Scala" var str2 = "Hello Scala" var str2_1 = "hello scala" //字符串比较 println(str1 == str2) println(str1.equals(str2)) println(str1.equalsIgnoreCase(str2_1)) //上述三个比较全部返回true //按字典顺序比较两个字符串 println(str1.compareTo(str3)) //按字典顺序比较两个字符串,不考虑大小写 println(str1.compareToIgnoreCase(str3)) //从0开始返回指定位置的字符 println(str1.charAt(6)) //追加 println(str2.concat(" Language")) //是否以指定的后缀结束 println(str1.endsWith("la")) //使用默认字符集将String编码为 byte 序列 println(str1.getBytes) //哈希码 println(str1.hashCode) //指定子字符串在此字符串中第一次出现处的索引 println(str1.indexOf("ca")) //字符串对象的规范化表示形式 println(str1.intern()) //指定子字符串在此字符串中最后一次出现处的索引 println(str1.lastIndexOf("al")) //长度 println(str1.length) //匹配正则表达式 println(str1.matches("d+")) //替换字符 println(str1.replace('a','o')) //根据字符切割, 需要注意Scala中从数组中取元素使用小括号 println(str1.split(" ")(1)) //是否以指定字符串开始 println(str1.startsWith("Hel")) //截取子字符串 println(str1.substring(3)) println(str1.substring(3,7)) //大小写 println(str1.toLowerCase()) println(str1.toUpperCase()) //去空格 println(str1.trim) //使用StringBuilder val strBuilder = new StringBuilder //拼接字符串 strBuilder.append("Hello ") strBuilder.append("Scala") println(strBuilder) //反转 println(strBuilder.reverse) //返回容量 println(strBuilder.capacity) //指定位置插入 println(strBuilder.insert(6,"Spark "))
Scala 集合
1. 数组
Java中使用 new String[]的形式可以创建数组, 但Scala中创建数组需要用到Array关键词, 用[ ]指定数组中元素的泛型, 取值使用小括号(index).
//创建Int类型的数组, 默认值为0 val nums = new Array[Int]() //创建String类型的数组, 默认值为null val strs = new Array[String]() //创建Boolean类型的数组, 默认值为false val bools = new Array[Boolean]() //通过索引遍历数组,给元素赋值 for (index <- 0 until nums.length) nums(index) = index + 1 //数组遍历,编码的逐步简化 nums.foreach ( (x: Int) => print(x + " ") ) println() nums.foreach ( (x => print(x + " ")) ) println() nums.foreach(print(_)) println() nums.foreach(print)
foreach函数传入一个函数参数, 由于Scala支持类型推测, 可以将参数函数的参数类型省略; 在参数函数中, 该函数的参数只出现一次, 因为可以使用下划线_代替(如果有多个可以使用_.1/_.2); 最后由于Scala语言的灵活性, 只需传入print这个函数也会遍历打印整个集合.
创建二维数组分两步: 创建一个泛型为数组的数组, 然后对这个数组遍历,
val secArray = new Array[Array[String]](5) for (index <- 0 until secArray.length){ secArray(index) = new Array[String](5) } //填充数据 for (i <- 0 until secArray.length;j <- 0 until secArray(i).length) { secArray(i)(j) = i * j + "" } secArray.foreach(array => array.foreach(println))
2. list
Scala中列表的定义使用List关键词. List集合是一个不可变的集合. 下面来看创建List已经list调用的方法.
//创建列表 val list = List(1,2,3,4,5) //对列表遍历 list.foreach(println) //contains判断是否包含某个元素 println(list.contains(6)) //反序,返回一个新的List list.reverse.foreach(println) //去前n个元素,返回一个新的List list.take(3).foreach(println) //删除前n个元素,返回一个新的List list.drop(2).foreach(println) //判断集合中是否有元素满足判断条件 println(list.exists(_ > 4)) //把List中的元素用设置的字符(串)进行拼接 list.mkString("==").foreach(print) /*map是一个高阶函数,需要一个函数参数 返回值是That,意思是谁调用的map返回的类型跟调用map方法的对象的类型一致 这里map返回的仍然是list,因此在map中可对每一个元素进行相同操作 map返回的list的泛型由编码传入的函数返回类型决定,如下(_ * )返回的list的泛型就是Int */ list.map(println) list.map(_ * ).foreach(println) val logList = List("Hello Scala" , "Hello Spark") /*由上述介绍可知,split()返回一个数组,因此map返回的类型是泛型为数组类型的list 需要对返回的list进行两次遍历,第一次遍历得到Array,第二次遍历拿到String */ logList.map(_.split(" ")).foreach(_.foreach(println)) /* 如果想直接拿到String,需要: 扁平操作 用到的函数是flatMap,flatMap返回的类型也是调用该方法的类型,但它可以直接得到String类型的单词 */ logList.flatMap(_.split(" ")).foreach(println) 对map和flatMap的理解可参考下图: Nil创建一个空List Nil.foreach(println) //::操作可用来添加元素 val list1 = 1::2::Nil list1.foreach(println) 需要注意的是, 上述创建的list均为不可变长度的list, 即list中的元素只有在创建时才能添加. 创建可变长度的list, 需要用到ListBuffer, 看代码: //创建一个ListBuffer,需要导包scala.collection.mutable.ListBuffer val listBuffer = new ListBuffer[String] //使用+=添加元素 listBuffer.+=("hello") listBuffer.+=("Scala") listBuffer.foreach(println) //使用-=去除元素 listBuffer.-=("hello")
3. set
Scala中使用Set关键词定义无重复项的集合.
Set常用方法展示:
//创建Set集合,Scala中会自动去除重复的元素 val set1 = Set(1,1,1,2,2,3) //遍历Set即可使用foreach也可使用for循环 set1.foreach(x => print( x + "\t")) val set2 = Set(1,2,3,5,7) //求两个集合的交集 set1.intersect(set2).foreach(println) set1.&(set2).foreach(println) //求差集 set2.diff(set1).foreach(println) set2.&~(set1).foreach(println) //求子集,如果set1中包含set2,则返回true.注意是set1包含set2返回true println(set2.subsetOf(set1)) //求最大值 println(set1.max) //求最小值 println(set1.min) //转成List类型 set1.toList.map(println) //转成字符串类型 set1.mkString("-").foreach(print)
4. Map
Scala中使用Map关键字创建KV键值对格式的数据类型.
4.1 创建map集合
val map = Map( "1" -> "Hello", 2 -> "Scala", 3 -> "Spark" )
创建Map时, 使用->来分隔key和value, KV类型可不相同, 中间使用逗号进行分隔.
4.2 map遍历
遍历map有三种方式, 即可使用foreach, 也可使用与Java中相同用法的迭代器, 还可使用for循环.
方式一: foreach
map.foreach(println)
此时, 打印的是一个个二元组类型的数据, 关于元组我们后文中会详细介绍, 此处只展示一下二元组的样子: (1,Hello); (2,Scala); (3,Spark).
方式二: 迭代器
val keyIterator = map.keys.iterator while (keyIterator.hasNext){ val key = keyIterator.next() println(key + "--" + map.get(key).get) }
此时需注意:
map.get(key)返回值, 返回提示:
an option value containing the value associated with key in this map, or None if none exists.
即返回的是一个Option类型的对象, 如果能够获取到值, 则返回的是一个Some(Option的子类)类型的数据, 例如打印会输出Some(Hello), 再通过get方法就可以获取到其值;
如果没有值会返回一个None(Option的子类)类型的数据, 该类型不能使用get方法获取值(本来就无值, 强行取值当然要出异常)
看get方法的提示(如下), 元素必须存在, 否则抛出NoSuchElementException的异常.
Returns the option's value. Note: The option must be nonEmpty.
Throws:
Predef.NoSuchElementException - if the option is empty.
既然这样, 对于None类型的数据就不能使用get了, 而是使用getOrElse(“default”)方法, 该方法会先去map集合中查找数据, 如果找不到会返回参数中设置的默认值. 例如,
//在上述map定义的情况下执行下述代码,会在终端打印default
println(map.get(4).getOrElse("default"))
1
2
方式三: for循环
for(k <- map) println(k._1 + "--" + k._2)
此处, 将map中的每一对KV以二元组(1, Hello)的形式赋给k这一循环变量. 可通过k._1来获取第一个位置的值, k._2获取第二个位置的值.
4.3 Map合并
//合并map val map1 = Map( (1,"a"), (2,"b"), (3,"c") ) val map2 = Map( (1,"aa"), (2,"bb"), (2,), (4,), (4,"dd") )
map1.++:(map2).foreach(println)
++和++:的区别
函数 调用 含义
++ map1.++(map2) map1中加入map2
++: map1.++:(map2) map2中加入map1
注意:map在合并时会将相同key的value替换
4.4 Map其他常见方法
//filter过滤,虑去不符合条件的记录 map.filter(x => { Integer.parseInt(x._1 + "") >= 2 }).foreach(println) //count对符合条件的记录计数 val count = map.count(x => { Integer.parseInt(x._1 + "") >= 2 }) println(count); /* 对于filter和count中条件设置使用Integer.parseInt(x._1 + "")是因为: * 定义map时,第一个key使用的是String类型,但在传入函数时每一个KV转化为一个二元组(Any,String)类型,x._1获取Any类型的值,+""将Any转化为String,最后再获取Int值进行判断. */ //contains判断是否包含某个key println(map.contains(2)) //exist判断是否包含符合条件的记录 println(map.exists(x =>{ x._2.equals("Scala") }))
5. 元组
元组是Scala中很特殊的一种集合, 可以创建二元组, 三元组, 四元组等等, 所有元组都是由一对小括号包裹, 元素之间使用逗号分隔.
元组与List的区别: list创建时如果指定好泛型, 那么list中的元素必须是这个泛型的元素; 元组创建后, 可以包含任意类型的元素.
创建元组即可使用关键字Tuple, 也可直接用小括号创建, 可以加 “new” 关键字, 也可不加. 取值时使用 "tuple._XX"获取元组中的值.
元组的创建和使用
//创建元组 val tuple = new Tuple1(1) val tuple2 = Tuple2("zhangsan",2) val tuple3 = Tuple3(1,2.0,true) val tuple4 = (1,2,3,4) val tuple = (1,2,3,4,5,6,7,8,9,,,,,,,,,) //注意:使用Tuple关键字最多支持个元素 val tuple = Tuple(1,2,3,4,5,6,7,8,9,,,,,,,,,,,,,) //使用 println(tuple2._1 + "\t" + tuple2._2) //元组中嵌套元组 val t = Tuple2((1,2),("zhangsan","lisi")) println(t._1._2) 元组的遍历 //tuple.productIterator可以得到迭代器, 然后用来遍历 val tupleIterator = tuple.productIterator while(tupleIterator.hasNext){ println(tupleIterator.next()) } toString, swap方法 //toString, 将元组中的所有元素拼接成一个字符串 println(tuple3.toString()) //swap翻转,只对二元组有效 println(tuple2.swap)
trait特性
Scala中的trait特性相对于Java而言就是接口. 虽然从功能上两者极其相似, 但trait比接口还要强大许多: trait中可以定义属性和方法的实现, 这点又有点像抽象类; Scala的类可以支持继承多个trait, 从结果来看即实现多继承.
Scala中定义trait特性与类相似, 不同在于需要使用"trait"关键字. 其他注意点在代码注释中做出说明:
trait Read { val readType = "Read" val gender = "m" //实现trait中方法 def read(name:String){ println(name+" is reading") } } trait Listen { val listenType = "Listen" val gender = "m" //实现trait中方法 def listen(name:String){ println(name + " is listenning") } } //继承trait使用extends关键字,多个trait之间使用with连接 class Person extends Read with Listen{ //继承多个trait时,如果有同名方法或属性,必须使用“override”重新定义 override val gender = "f" } object test { def main(args: Array[String]): Unit = { val person = new Person() person.read("zhangsan") person.listen("lisi") println(person.listenType) println(person.readType) println(person.gender) } } object Lesson_Trait2 { def main(args: Array[String]): Unit = { val p1 = new Point(1,2) val p2 = new Point(1,3) println(p1.isEqule(p2)) println(p1.isNotEqule(p2)) } } trait Equle{ //不实现trait中方法 def isEqule(x:Any) :Boolean //实现trait中方法 def isNotEqule(x : Any) = { !isEqule(x) } } class Point(x:Int, y:Int) extends Equle { val xx = x val yy = y def isEqule(p:Any) = { /* * isInstanceOf:判断是否为指定类型 * asInstanceOf:转换为指定类型 */ p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx } }
模式匹配match-case
Scala中的模式匹配match-case就相当于Java中的switch-case. Scala 提供强大的模式匹配机制, 即可匹配值又可匹配类型. 一个模式匹配包含一系列备选项, 每个备选项都以case关键字开始. 并且每个备选项都包含了一个模式以及一到多个表达式, 箭头符号 => 隔开了模式和表达式。
object Lesson_Match { def main(args: Array[String]): Unit = { val tuple = Tuple7(1,2,3f,4,"abc",d,true) val tupleIterator = tuple.productIterator while(tupleIterator.hasNext){ matchTest(tupleIterator.next()) } } /** * 注意 * 1.模式匹配不仅可以匹配值,还可以匹配类型 * 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配 * 3.模式匹配中,都匹配不上时,会匹配到case _ ,相当于default */ def matchTest(x:Any) ={ x match { //匹配值 case 1 => println("result is 1") case 2 => println("result is 2") case 3 => println("result is 3") //匹配类型 case x:Int => println("type is Int") case x:String => println("type is String") case x :Double => println("type is Double") case _ => println("no match") } } }
由于匹配到对应的类型或值时, 就不再继续往下匹配, 所有在编写备选项时要将范围小的放在前面, 否则就会失去意义. 这就类似于try-catch中处理异常时, 也要先从小范围开始.
样例类case classes
使用case关键字定义的类就是样例类(case classes), 样例类实现类构造参数的getter方法 (构造参数默认被声明为val) , 当构造参数类型声明为var时, 样例类会实现参数的setter和getter方法.
样例类默认实现toString, equals, copy和hashCode等方法. 样例类在创建对象时可new, 也可不new.
//使用case关键字定义样例类 case class Person(name:String, age:Int) object Lesson_CaseClass { def main(args: Array[String]): Unit = { //创建样例类对象,可new可不new val p1 = new Person("zhangsan",) val p2 = Person("lisi",) val p3 = Person("wangwu",) val list = List(p1,p2,p3) list.foreach { x => { x match { case Person("zhangsan",) => println("zhs") case Person("lisi",) => println("lisi") case p:Person => println("is a person") case _ => println("no match") } } } } }
并发 Actor Model
Actor Model相当于Java中的Thread, Actor Model用来编写并行计算或分布式系统的高层次抽象. Actor不需要担心多线程模式下共享锁的问题, 可用性极高.
Actors将状态和行为封装在一个轻量级的进程/线程中, 但它不和其他Actors分享状态, 每个Actors有自己的世界观, 当需要和其他Actors交互时, 通过发送异步的, 非堵塞的(fire-and-forget)事件和消息来交互. 发送消息后不必等另外Actors回复, 也不必暂停, 每个Actors有自己的消息队列, 进来的消息按先来后到排列, 这就有很好的并发策略和可伸缩性, 可以建立性能良好的事件驱动系统.
Actor的特征:
ActorModel是消息传递模型,基本特征就是消息传递
消息发送是异步的,非阻塞的
消息一旦发送成功,不能修改 (类似发邮件)
Actor之间传递时,自己决定决定去检查消息,而不是一直等待,是异步非阻塞的
定义Actor需要继承Actor这一trait, 实现act这一方法, 并且使用感叹号! 来发送消息.
一个简单实例:
import scala.actors.Actor //自定义Actor class myActor extends Actor{ def act(){ while(true){ receive { case x:String => println("save String ="+ x) case x:Int => println("save Int") case _ => println("save default") } } } } object Lesson_Actor { def main(args: Array[String]): Unit = { //创建actor的消息接收和传递 val actor =new myActor() //启动 actor.start() //发送消息写法 actor ! "Hello Scala Actor !" } }
Actor与Actor之间通信:
//创建样例类,用来发送 case class Message(actor:Actor,msg:Any) class Actor1 extends Actor{ def act(){ while(true){ //对接收的消息进行模式匹配 receive{ case msg :Message => { println("i sava msg! = "+ msg.msg) //回复消息 msg.actor!"i love you too !" } case msg :String => println(msg) case _ => println("default msg!") } } } } //为了实现Actor中的通信,需要拿到另一个Actor的对象 class Actor2(actor :Actor) extends Actor{ //发送消息 actor ! Message(this,"i love you !") def act(){ while(true){ receive{ case msg :String => { if(msg.equals("i love you too !")){ println(msg) actor! "could we have a date !" } } case _ => println("default msg!") } } } } object Lesson_Actor2 { def main(args: Array[String]): Unit = { val actor1 = new Actor1() actor1.start() val actor2 = new Actor2(actor1) actor2.start() } }
Scala隐式转换系统
隐式转换是指在编写程序时, 尽量少的去编写代码, 让编译器去尝试在编译期间自动推导出某些信息来, 这就类似于在Scala中定义变量时不需要指定变量类型. 这种特性可以极大的减少代码量, 提高代码质量.
Scala中提供强大的隐式转换系统, 分别为: 隐式值, 隐式视图和隐式类.
隐式值
先来看一个隐式值的Demo:
object Lesson_Implicit1 { def main(args: Array[String]): Unit = { implicit val name = "Scala Study" sayName } def sayName(implicit name:String) = { println("say love to " + name) } }
这里将name变量声明为implicit, 编译器在执行sayName方法时发现缺少一个String类型的参数, 此时会搜索作用域内类型为String的隐式值, 并将搜索到的隐式值作为sayName的参数值进行传递.
需要注意:
隐式转换必须满足无歧义规则, 否则会报错:
ambiguous implicit values: both value a of type String and value name of type String match expected type String
在同一个作用域禁止声明两个类型一致的变量,防止在搜索的时候会犹豫不决
声明隐式参数的类型最好是自定义的数据类型,一般不要使用Int,String这些常用类型,防止碰巧冲突.
隐式视图
隐式视图就是把一种类型自动转换为另一种类型. 还是先来看代码:
object Lesson_Implicit2 { def main(args: Array[String]): Unit = { //声明隐式视图 implicit def stringToInt(num:String) = Integer.parseInt(num) println(addNum("")) } def addNum(num:Int) = { num + } }
这里addNum方法参数是String类型, 不符合定义要求, 此时编译器搜索作用域发现有个隐式方法, 正好这个方法的参数是String, 返回是Int. 然后就会调用这个隐式方法, 返回一个Int值并将它传给addNum方法.
隐式类
隐式类是指把一个对象自动转换为另一种类型的对象, 转换后可以调用原来不存在的方法.
package com.qb.scala object Lesson_Implicit3 { def main(args: Array[String]): Unit = { //导入隐式类所在的包 import com.qb.scala.Util.StringLength println("qwer".getLength()) } } object Util { //定义一个隐式类,使用implicit关键字修饰 implicit class StringLength(val s : String){ def getLength() = s.length } }
这里编译器在qwer对象调用getLength方法时, 发现该对象并没有getLength方法, 此时编译器发现在作用域范围内有隐式实体. 发现有符合的隐式类可以用来转换成带有getLength方法的Util类, 进而就可调用getLength方法.
需要注意:
隐式类所带的构造参数有且只能有一个
必须在类, 伴生对象和包对象中定义隐式类
隐式类不能是case class(样例类), case class 在定义时会自动生成伴生对象.
作用域中不能有与隐式类同名的标识符
感谢大家支持,之后会持续输出技术干货!