Você está na página 1de 145

Fundamentos de Programação

em Ambiente de Big Data

Jose Luiz Rosa (jlr.informatica@gmail.com)


■ Componentes e bibliotecas Apache Spark

.
Spark

■ Construído em Scala
■ Roda em uma JVM (Java Virtual Machine)
■ Aplicações: Driver e Executors
■ Driver: processo que é responsável por:
- Manter informações sobre a aplicação Spark;
- Responder a entradas dos programas do usuário
- Analisar, distribuir e agendar os trabalhos nos
executores
.
SparkSession

– Gerencia aplicações Spark


– Possibilita o envio de comandos e dados à
aplicação Spark
DataFrames

- É a API mais comum


- Representa uma tabela de dados com linhas (rows) e
colunas (columns)

.
Transformações

– Em Spark, dados são imutáveis


– Novos dados são obtidos por meio da
modificação, ou transformação, dos dados
anteriores
Partições

– Para um processamento paralelo efetivo, os


dados devem ser particionados
– Cada partição contém dados a serem trabalhados
por um executor
– Uma partição somente significa não haver
paralelismo, pois o trabalho ocorrerá somente
em um executor
– Raciocínio análogo para muitas partições e
somente um executor
Ações
Com Spark, ao contrário de Hadoop, pode-se
encadear uma sequência de estágios (pipeline)
utilizando um mecanismo de DAG (Directed Acyclic
Graph, ou, Grafo Acíclico Direcionado) avançado, o
que permite a execução de fluxos de dados e
computação in-memory.
DAG
– Um DAG é um grafo acíclico (não há ciclos, ou
seja, loops). Isto significa dizer que, partindo-se
de um nó A para um nó B, não se consegue
retornar ao nó A. Ele é direcionado porque o
percurso realizado no grafo possui direção, ou
seja, partir do nó A para o nó B não produz o
mesmo resultado que partir do nó B em direção
ao nó A. Dessa forma, os nós de um sistema que
executa seus jobs utilizando DAG podem rodar de
forma paralela.
– Spark realiza operações MapReduce (Spark Core),
consultas SQL (SparkSQL), aprendizado de
máquina (MLlib), streaming de dados (Spark
Streaming) e processamento sobre grafos
(GraphX), podendo-se combinar tais bibliotecas
na mesma aplicação.
– Spark foi construído com a linguagem de
programação Scala, mas pode-se construir
aplicações em Spark utilizando também as
linguagens Scala, Java, Phyton ou R, apesar de só
possuir, atualmente, shell interativo para Scala e
Phyton.
RDD
O Spark é dividido em dois grandes componentes:
- Driver: Gerencia a execução do processo. Recebe as
requisições de usuário. Dispara os jobs para os
executores.
- Executores: São as unidades de processamento
(DataNodes). Os executores possuem os dados a
serem processados por Spark.
RDD
Toda aplicação Spark consiste de um driver, que
executa a função “main” e as várias operações
paralelas no cluster. A principal abstração fornecida
por Spark, RDD (Resilient Distributed Dataset), é uma
base de dados distribuída tolerante a falhas, ou seja,
uma coleção de elementos particionados,
armazenados nos nós executores, os quais operam
sobre esses dados de forma paralela.
– Outra característica de Spark, é que os dados de
um RDD são armazenados em diferentes
partições, em um ou mais executores. Por
exemplo, seja um cluster com quatro executores
(representados por quadrados):
Executor 1 Executor 2

Executor 3 Executor 4
– Suponha haver três RDDs, cujas partições sejam
representadas pelas formas: quadrado: , círculo
e triângulo .
RDD 1
RDD 2

RDD 3
■ Os RDDs são imutáveis, então, uma vez criados,
não se pode alterá-los, e sim, criar novos RDDs a
partir de RDDs existentes (operação conhecida por
transformação).

■ Os RDDs são processados pelo Spark, que gera


novos RDDs a partir da transformação dos dados por
meio de funções.
■ RDD suporta dois tipos de operações: Transformação e
Ação.
- Transformação: Não retornam um único valor, mas um novo
RDD. Nada é avaliado quando a função de transformação é
invocada: apenas um novo RDD é retornado a partir de um
RDD antigo. São funções de transformação, por exemplo:
map, filter, flatMap, groupByKey e reduceByKey.
- Ação: Esta operação avalia e retorna um novo valor. Quando
uma função de ação é invocada, todas as transformações são
processadas, e um valor é retornado. São funções de ação,
por exemplo: collect, count, first, foreach e reduce.
As operações sobre os RDDs vão, então,
formando um grafo (DAG) de tarefas a serem
executadas em blocos distribuídos de
memória. A execução das ações é realizada
de forma tardia (lazy), com os resultados
intermediários sendo mantidos em memória.
Caso haja falha na execução de uma dada
tarefa, basta que Spark reprocesse o grafo
para que a tarefa seja cumprida, o que
garante resiliência ao RDD.
O acesso ao disco é realizado em último caso,
e somente se a memória disponível no
sistema não mais comportar a quantidade de
dados a serem armazenados. O mecanismo
de realizar transformações e só processá-las
invocando-se uma função de ação, explica a
diferença de tempo de processamento entre
Spark e Hadoop, pois isso evita a alta
latência requerida por Hadoop com suas
operações de disco.
Criando RDDs
No Scala, crie um RDD a partir de um array contendo
números inteiros de 1 a 5 e, depois, paralelize esses
dados por meio do método paralelize do objeto sc
(SparkContext). Este objeto, sc, é o ponto de entrada
para uma aplicação Spark, e é criado
automaticamente quando da execução de spark-shell
na janela do console.
Carregando dados de arquivo
scala> val enders = sc.textFile("enderecos.txt")

Agora, cria-se o RDD caractLinha, que conterá a


quantidade de caracteres, linha a linha, do RDD
enders:

scala> val caractLinha = enders.map(x => x.length)


Transformações
Funções de transformação permitem obter novos
RDDs a partir de RDD anteriores. Abaixo são listadas
algumas funções de transformação suportadas por
Spark. A função collect (ação) será executada após as
transformações, para que o resultado seja exibido.
map (func)
Retorna um novo RDD formado pelos retornos da
função func aplicada a cada elemento do RDD
original.
scala> sc.parallelize(Array(("RJ",2), ("SP",1), ("SP",2), ("MG",4), ("RJ",6),
("ES",8), ("RJ",8))).groupByKey().map(x => (x._1,x._2.sum)).collect

resulta em:
Array[(String, Int)] = Array((RJ,16), (SP,4), (ES,8), (MG,4))
filter(func)
Retorna um novo RDD formado pelos elementos do RDD origem que
satisfazem a função func (elementos que fazem a func retornar true).

scala> sc.parallelize(Array(("Leite",5.00), ("Margarina",3.99), ("Pão",0.30),


("Queijo",27.50))).filter(x => x._2 < 5.00).collect

resulta em:

res0: Array[(String, Double)] = Array((Margarina,3.99), (Pão,0.3))


flatMap(func)
Similar à map, mas cada item pode ser mapeado para 0 ou mais
itens de saída. A função func deve retornar uma sequência, ao
invés de um único item. Por exemplo:

scala> sc.parallelize(Array(("RJ",2), ("SP",1), ("SP",2), ("MG",4),


("RJ",6), ("ES",8), ("RJ",8))).flatMap(x => List(x._1)).collect

resulta em:

res129: Array[String] = Array(RJ, SP, SP, MG, RJ, ES, RJ)


union(otherDataset)
Retorna um novo RDD contendo os elementos de ambos os RDDs. Por
exemplo, dados os RDDs:

scala> val est1 = sc.parallelize(Array(("BA",1), ("SE",2)))

scala> val est2 = sc.parallelize(Array(("GO",3), ("DF",4)))

executar:

scala> est1.union(est2).collect

resulta em:

res2: Array[(String, Int)] = Array((BA,1), (SE,2), (GO,3), (DF,4))


reduceByKey(func, [numTasks])
Atua sobre um RDD contendo pares (K,V). Retorna um RDD de
pares (K,V), onde os valores para cada chave são agregados
utilizando a função de redução, a qual pode ser do tipo (V,V) => V.
Por exemplo:

scala> sc.parallelize(Array(("RJ",2), ("SP",1), ("SP",2),

("MG",4), ("RJ",6), ("ES",8), ("RJ",8))).reduceByKey(_+_).collect

resulta em:

res54: Array[(String, Int)] = Array((RJ,16), (SP,3), (ES,8), (MG,4))


Ações
Funções de ação avaliam e retornam um novo valor.
Quando uma função de ação é invocada, todas as
transformações são processadas, e um valor é
retornado.
reduce(func)
Agrega os elementos do RDD por meio de func (a
qual utiliza dois argumentos e retorna somente um).
A função func deve ser comutativa e associativa, para
que possa ser computada em paralelo.
scala> sc.parallelize(Array(1, 2, 3, 4, 5)).reduce( (a, b) => a+b )
res0: Int = 15
Ou, pode-se utilizar o caractere underscore (_):

scala> sc.parallelize(List(1, 2, 3, 4, 5)).reduce( _ + _ )


res1: Int = 15
collect()
Retorna todos os elementos do RDD como um array
ao Driver. Esta função é muito útil após a execução
de filter, ou outra operação que retorne um pequeno
subconjunto de dados.
scala> sc.parallelize(List(1, 2, 3, 4, 5)).collect

res2: Array[Int] = Array(1, 2, 3, 4, 5)


Para utilizar o Spark deve-se conectá-lo ao
aplicativo que contém o código de manipulação
(transformação/ação) dos dados. Conecta-se o
Spark ao aplicativo por meio de uma instância de
SparkContext, que o ambiente interativo do Spark
(spark-shell) chama de sc, para criar/manipular os
RDDs, que são abstrações que representam as
coleções de dados distribuídos.
Visando facilitar o processamento de dados
estruturados, Spark oferece um módulo denominado
Spark SQL. Há muitas formas de interagir com Spark
SQL: por meio de SQL e por meio da API Dataset.
Dataset

A nova interface de Spark SQL, Dataset, disponível


em Scala e Java, é uma coleção de dados distribuída,
e provê os benefícios do RDD: uso de funções
Lambda e a consistência de dados e memória (strong
typing), com os benefícios do motor de execução
otimizado de Spark SQL.
Dataset

Ao contrário da API simples dos RDDs de Spark,


Dataset conta com mais informação sobre os dados e
sobre o processamento sendo executado sobre eles.
Internamente, Spark SQL utiliza essa informação para
realizar otimizações adicionais sobre o
processamento.
DataFrame

Um Dataset organizado em termos de colunas


nomeadas é chamado de DataFrame. Conceitualmente,
um DataFrame é equivalente à tabela no modelo de
banco de dados relacional, porém, bem mais
otimizado.
DataFrame

Em Scala ou Java, um DataFrame é representado por


um Dataset de linhas (rows) descritas por um
esquema. Em Scala, um DataFrame é um alias para o
tipo Dataset[Row]. Podem ser construídos a partir de
RDDs, tabelas do Hive ou arquivos com dados
estruturados e semiestruturados, ou seja, um
DataFrame é um Dataset.
DataFrames proveem uma Linguagem Específica de
Domínio (DSL: Domain-Specific Language) para
manipulação de dados estruturados, em Scala, Java,
Python e R. Essas operações são conhecidas como
“transformações não tipadas”, em contraste com as
“transformações tipadas”, que utilizam os Datasets
fortemente tipados de Scala/Java.
Um DataFrame é representado por um Dataset de
linhas (rows) descritas por um esquema. Uma vez
associado a um esquema, pode-se criar uma tabela,
uma abstração que permite a manipulação dos
“campos” via SQL.
Pode-se obter um DataFrame associado a um
esquema de dados a partir de um RDD de Rows:
import org.apache.spark.sql.types._
import org.apache.spark.sql.Row

val rdd =
sc.parallelize(Seq(Seq("A",65),Seq("B",66)).map(Row.fromSeq(_)))

val schema = StructType(Seq(StructField("letra", StringType),


StructField("cod", IntegerType)))
val df = spark.createDataFrame(rdd,schema)
case class CCAscii(letra: String, cod: Int)
val encoder = org.apache.spark.sql.Encoders.product[CCAscii]
val ds = df.as(encoder)
Testando o código

Para testar o código anterior, vamos clonar o ambiente


de estudos a partir de imagens Docker do Fabio Jardim
(Github):

1) Utilize o Linux ou, no caso do Windows, o WSL ou


uma máquina virtual Linux (VirtualBox ou VMWare).

2) Crie um diretório chamado docker e torne-o corrente


3) Faça o clone do ambiente bigdata:

git clone https://github.com/fabiogjardim/bigdata_docker.git

4) Altere as pemissões da pasta gerada na clonagem:

chmod 777 bigdata_docker

5) Torne o diretório bigdata_docker corrente:

cd bigdata_docker/
6) Baixe as imagens:

docker-compose pull

7) Inicie o cluster Hadoop (execução dos contêineres) através do


docker compose (Com a opção "-d" você inicia todos os serviços
em background):

docker-compose up –d
8) Para executar códigos Scala:

docker exec -it jupyter-spark bash

A imagem jupyter-spark contém o ambiente que precisamos para


executar códigos Scala.
9) Executar spark-shell:

No prompt do Spark, execute o ambiente de execução interativo


de comandos Scala (ou REPL: Read-Eval-Print Loop) :
root@jupyter-spark:/# spark-shell
As seguintes informações sobre versão são exibidas:
Spark: 2.4.1 Scala: 2.11.12 Java: 1.8.0_201
Os valores podem mudar, em função de instalações diferentes das
apresentadas aqui.
10) Ao surgir o prompt do Scala, execute cada um dos comandos
mostrados anteriormente (pressione ENTER ao final de cada
linha):
Comandos:

import org.apache.spark.sql.types._
import org.apache.spark.sql.Row
val rdd =
sc.parallelize(Seq(Seq("A",65),Seq("B",66)).map(Row.fromSeq(_)))
val schema = StructType(Seq(StructField("letra", StringType),
StructField("cod", IntegerType)))
val df = spark.createDataFrame(rdd,schema)
df.show
11) Crie o Dataset “ds” a partir do Dataframe “df”, crie uma view
de nome “tabela” e filtre (transformação) os dados de “ds”,
criando o dataset “ds1”, com os dados de “ds” ordenados pelo
campo “letra” em ordem invertida:

case class CCAscii(letra: String, cod: Int)


val encoder = org.apache.spark.sql.Encoders.product[CCAscii]
val ds = df.as(encoder)
ds.createOrReplaceTempView("tabela")
val ds1 = spark.sql("""SELECT * FROM tabela order by letra desc""")
ds1.show
Comandos:
Linguagem Scala
Linguagem funcional, orientada a objetos e de fácil
integração com Java. Essa integração fica evidenciada
pelo fato de todas as classes do pacote java.lang serem
importadas por padrão. As demais classes, entretanto,
devem ser importadas de forma explícita.
Antes de começar, importe o pacote sys.process, de tal
forma que possamos executar comandos de sistema
(sempre entre aspas, terminando com um ponto de
exclamação). Por exemplo, para “limpar” a tela com o
comando “clear”, faz-se como abaixo:

scala> import sys.process._


Para limpar a “tela”:
scala> "clear" !
Variáveis

Podem ser declaradas de duas formas:

val: uma vez recebido um valor, a variável não pode mais


ser alterada

var: a variável pode ser alterada após receber um valor


Como uma variável declarada por val não pode ser
alterada, fazer:
scala> val a = 1
scala> a = 2
gera a seguinte mensagem de erro:
<console>:32: error: reassignment to val
a = 2;
^
Uma variável pode ter seu valor avaliado dentro de uma
String por meio do interpolador “s”. Por exemplo:
scala> val a = 1
scala> val b = 1.0
scala> val tipos = s"a = $a e b = $b"
scala> println (tipos)
a = 1 e b = 1.0
Neste caso, “$a” foi substituída por “1” e “$b” por “1.0”.
Observe o caractere interpolador “s” antes das aspas
que abrem a String (sem espaço em branco). Caso
não haja “s” antes das aspas, a String é impressa tal
como está escrita (sem avaliação das variáveis):

scala> val tipos = "a = $a e b = $b"

scala> println (tipos)

a = $a e b = $b
Observe o caractere interpolador “s” antes das aspas
que abrem a String (sem espaço em branco). Caso
não haja “s” antes das aspas, a String é impressa tal
como está escrita (sem avaliação das variáveis):

scala> val tipos = "a = $a e b = $b"

scala> println (tipos)

a = $a e b = $b
Tipos de Dados

Os tipos de dados em Scala iniciam com letra


maiúscula
Tipos de Dados

- String: sequência de caracteres delimitada por


aspas duplas. Por exemplo, abaixo a função
println “escreve” a string teste:

scala> println ("teste")

teste
Outro exemplo: concatenando strings com o
operador “+”:

scala> import java.util.Date

import java.util.Date

scala> println ("Data: " + new Date)

Data: Tue Nov 01 00:16:04 UTC 2016


■ x- Char caractere delimitado por aspas simples (apóstrofe).

- Byte -128 a 127

- Short -32768 a 32767

- Int -2147483648 a 2147483647

- Long -9223372036854775808 a 9223372036854775807

- Float ponto flutuante de simples precisão

- Double ponto flutuante de dupla precisão

- Boolean valor lógico true ou false


Não é preciso explicitar o tipo da variável, pois Scala
tem alta capacidade de inferência. Por exemplo:

val a = 1 => Scala define a como Int

val b: Int = 1 => b: Int define b como Int


Operadores Aritméticos

+ : adição

- : subtração

* : multiplicação

/ : divisão

% : resto da divisão
Operadores Relacionais
= : atribuição
== : igual a
!= : diferente de
> : maior que
>= : maior ou igual a
< : menor que
<= : menor ou igual a
Operadores Lógicos

&& : e

|| : ou

! : não
Outros Operadores
+= : adiciona o valor à direita à variável à esquerda, e
atribui à variável o valor resultante
Por exemplo:
scala> var x = 1
x: Int = 1
scala> x += 4
scala> println (x)
5
Outros Operadores
-= : subtrai o valor à direita da variável à esquerda, e
atribui à variável o valor resultante
*= : multiplica o valor à direita à variável à esquerda, e
atribui à variável o valor resultante
/= : divide o valor da variável à esquerda pelo valor à
direita, e atribui à variável o valor resultante
%= : calcula o resto da divisão da variável à esquerda
pelo valor à direita, e atribui à variável o valor resultante
Estrutura de Controle: if

Sintaxe: if (condição) { bloco1 } else { bloco2}

Exemplo:

scala> val a = 0

scala> if (a == 0) { println ("a não pode ser nulo!")}


Obs.: Se o bloco contiver somente um comando, as
chaves podem ser dispensadas. Assim, executar:

scala> if (a == 0) {println ("a não pode ser nulo!")}

produz o mesmo que:

scala> if (a == 0) println ("a não pode ser nulo!")


Estrutura de Controle: for

Sintaxe: for (var <- início to fim) { bloco }

Exemplo 1:

scala> for ( a <- 1 to 3) {println (a * 5)}

10

15
Exemplo 2:
for (a <- List(1,2,3)) println (a * 2)
2

6
Interpolador
O interpolador “s” avalia uma variável dentro de uma
String. Por exemplo:
scala> val a = 1
scala> val b = 1.0
scala> val tipos = s"a = $a e b = $b"
scala> println (tipos)
a = 1 e b = 1.0
Já o interpolador “f”, avalia uma variável ou
expressão, e pode formatá-la conforme o
especificador de formato escolhido (tal como
utilizado no comando printf da linguagem C).
Por exemplo, se o valor da variável “b” deve ser
impresso com três casas decimais:
scala> val a = 1
scala> val b = 1.0
scala> val tipos = f"a = $a%d e b = $b%1.3f"
scala> println (tipos)
a = 1 e b = 1.000
Função
A declaração de uma função tem a seguinte sintaxe, e inicia
pela palavra-chave def:
def nomeDaFunção () : tipoDaFunção = valorDeRetorno
Exemplo:
scala> def num10 () : Int = 10
scala> println (num10)
10
Podem ser especificados parâmetros:

def nomeDaFunção (par1 : Tipo1 , par2 : Tipo2) : ...

Exemplo:

scala> def soma (a : Int , b : Int) : Int = a + b

scala> println ( soma (2 , 3) )

5
Uma função que não retorna valor, é uma função do
tipo Unit (equivalente ao void de Java).
Neste caso, o tipo de retorno da função pode ser
declarado como Unit: def msg (m : String) : Unit.
Ou pode ser omitido.
Exemplo:

scala> def msg (m : String) = { println (m) }

msg: (m: String)Unit

scala> msg("Alô você!")

Alô você!
Para conter mais de uma sentença, o corpo da função
tem que estar delimitado por chaves. Por exemplo:
scala> def somaTodosNums (n : Int) : Int = {
var soma = 0
for (x <- 1 to n)
soma += x
return soma
}
Funções de Primeira Classe e Funções de Alta Ordem

Uma função é dita de primeira classe, se ela pode ser


armazenada em uma variável, passada como
parâmetro de uma função ou retornada como
resultado do processamento de uma outra função.

Funções que recebem outras funções por parâmetro,


ou cujo valor de retorno é uma função, são ditas
funções de alta ordem.
Exemplo: Seja a função anterior, “somaTodosNums”.
Vamos criar uma variável “stn” que receba essa
função, e que opere o mesmo valor (10):

scala> val stn = somaTodosNums(_)

stn: Int => Int = <function1>

scala> stn(10)

res2: Int = 55
Outro exemplo: criar a função “opera”, que recebe
por parâmetro a função “fc” (recebe um inteiro e
retorna um inteiro), e um valor do tipo inteiro, “x”. A
função “fc” deve receber - por parâmetro - o valor
“x”:

scala> def opera (fc : Int => Int , x : Int) = fc(x)


Em seguida, definem-se as funções dobro e
triplo, que calculam, respectivamente, o dobro
e o triplo do valor passado como parâmetro:

scala> def dobro (n : Int) : Int = n * 2


scala> def triplo (n : Int) : Int = n * 3
Para finalizar, invoca-se a função “opera”, passando
como parâmetro a função desejada (dobro ou triplo)
e um valor numérico do tipo Int.

scala> opera(dobro,2)

res5: Int = 4

scala> opera(triplo,2)

res6: Int = 6
scala> def opera (fc : Int => Int , x : Int) = fc(x)

opera: (fc: Int => Int, x: Int)Int

scala> def dobro (n : Int) : Int = n * 2

dobro: (n: Int)Int

scala> def triplo (n : Int) : Int = n * 3

triplo: (n: Int)Int

scala> opera(dobro,2)

res5: Int = 4scala> opera(triplo,2)

res6: Int = 6
Funções Anônimas

Funções anônimas, ou funções Lambda, são funções


que não possuem nome.
Declara-se uma função anônima como:
(parâmetro : Tipo) => retorno da função
Exemplo:

scala> val triplo = (n : Int) => n * 3

triplo: Int => Int = <function1>

scala> triplo (5)

res9: Int = 15
Definindo a funcionalidade a posteriori:

scala> def opera(f:(Int => Int), n:Int) = f(n)

opera: (f: Int => Int, n: Int)Int

scala> opera(x=>x+1,2)

res10: Int = 3

scala> opera(x=>x*2,2)

res11: Int = 4
Classe
Classes podem ser declaradas por meio da palavra-chave class.
Podem ser definidos parâmetros, que devem ser passados no
momento em que a classe está sendo instanciada, em substituição
ao método construtor.

Os métodos são funções e, por isso, iniciam sua declaração pela


palavra-chave def. Métodos e campos possuem, por padrão,
visibilidade public, mas podem ser protected ou private.

Os argumentos do construtor (entre parênteses, após o nome da


classe) são private.
Exemplo:
class Numeros (a : Int , b : Int) {

private var d = 0

def soma() : Int = a + b

def subtracao() : Int = a - b

def multiplicacao() : Int = a * b

def isValido : Boolean = if (b != 0) true else false

def divisao() : Int = if (isValido) a /b else 0

}
scala> val num = new Numeros (10,2)

num: Numeros = $iwC$$iwC$Numeros@138725bc

scala> num.soma()

res8: Int = 12

scala> num.subtracao()

res9: Int = 8
Case Class
São classes que não precisam da palavra-chave
“new” para serem instanciadas.
Exemplo: Seja a Case Class Soma (SomaCC):

scala> case class SomaCC (x : Int , y : Int) {

def soma() : Int = x + y

}
Obter a soma entre 1 e 2, por meio de SomaCC (sem
utilizar a palavra-chave “new”):

scala> SomaCC(1,2).soma()

res8: Int = 3
Array
Arrays são coleções de dados de mesmo tipo. Variáveis
do tipo Array são variáveis indexadas, com o primeiro
índice igual a 0 (zero). Podem conter elementos
duplicados e são modificáveis.

Declaração:
scala> var arr = new Array[String](2)

arr: Array[String] = Array(null, null)


scala> arr (0) = "Texto 1"

scala> arr (1) = "Texto 2"

scala> arr(0)

res54: String = Texto 1

scala> arr(1)

res55: String = Texto 2


Também é possível definir os elementos do Array no
momento da sua declaração:
scala> var arr = Array("Texto 1" , "Texto 2")
scala> arr(0)
res56: String = Texto 1
scala> arr(1)
res57: String = Texto 2
Obs.: Arrays contendo dados de tipos diferentes são
Arrays do tipo Any:

scala> var arr = Array("Texto 1" , 2)


arr: Array[Any] = Array(Texto 1, 2)
scala> arr(0)
res64: Any = Texto 1
scala> arr(1)
res65: Any = 2
Você pode utilizar os elementos do tipo String
diretamente:

scala> “Pos 0 do array: " + arr(0)


res71: String = Pos 0 do array: Texto 1
Porém, ao tentar utilizar dados de outros tipos:
scala> arr(1)+1
<console>:29: error: type mismatch;
found : Int(1)
required: String
arr(1)+1
^
Neste caso, deve-se converter o tipo do dado de
Any para seu tipo, por meio de asInstanceOf[Tipo]:

scala> arr(1).asInstanceOf[Int] + 1
res69: Int = 3
A iteração do array pode ser realizada pelo comando
“for”:

scala> val elementos = Array(1,2,3)

elementos: Array[Int] = Array(1, 2, 3)

scala> for (e <- elementos) println (e)


1
2
3
Tuplas
São coleções de elementos de mesmo tipo ou de
diferentes tipos de dado.

Exemplo:

scala> val aluno = (1, "José")

aluno: (Int, String) = (1,José)


Os elementos da tupla podem ser acessados
especificando-se o nome da variável, seguido do
caractere ponto, do caractere sublinhado (underscore) e
da posição em que se encontra o elemento desejado
(com o primeiro índice igual a 1):
scala> println ("Nome do aluno: " + aluno._2)

Nome do aluno: José

scala> println ("Ordem na chamada: " + aluno._1)

Ordem na chamada: 1
Listas
Listas são similares a arrays, com a diferença que
listas são imutáveis, ou seja, não podem sofrer
alterações após criadas. Pode-se criar novas listas a
partir de listas existentes, mas, não, alterar uma lista
já definida.
Exemplo:

scala> val listagem : List[Int] = List(1,2,3,4,5)

listagem: List[Int] = List(1, 2, 3, 4, 5)


Uma lista também pode ser definida em termos da
função “cons” (::). Lembre-se que Scala é funcional, e
tudo em Scala é uma função. Em termos práticos:

scala> val novaListagem = 0 :: listagem

novaListagem: List[Int] = List(0, 1, 2, 3, 4, 5)


O último elemento de uma lista é o elemento “Nil”:

scala> val listagem = 1 :: (2 :: (3 :: (4 :: (5 :: Nil ))))

listagem: List[Int] = List(1, 2, 3, 4, 5)

Observe o balanceamento dos parênteses:


Funções aplicáveis a listas

Função: ::

A função cons (::) retorna uma nova lista contendo o


elemento especificado no início da lista.

scala> val lista = List(1,2,3,3)

scala> val novaLista = 0 :: lista

novaLista: List[Int] = List(0, 1, 2, 3, 3)


Função: ::

A função cons (::) retorna uma nova lista contendo o


elemento especificado no início da lista.

scala> val lista = List(1,2,3,3)

scala> val novaLista = 0 :: lista

novaLista: List[Int] = List(0, 1, 2, 3, 3)


Função: apply

Retorna o elemento do índice especificado.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.apply(1)

novaLista: Int = 2
Função: distinct

Retorna uma lista sem elementos duplicados.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.distinct

novaLista: List[Int] = List(1, 2, 3)


Função: drop

Retorna os elementos a partir da posição


especificada até o final. A primeira posição é 0 (zero).

scala> val lista = List(1,2,3,3)

scala> val novaListagem = lista.drop(2)

novaListagem: List[Int] = List(3, 3)


Função: dropRight

Retorna todos os elementos da lista, exceto os n


últimos.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.dropRight(3)

novaLista: List[Int] = List(1)


Função: dropWhile

Exclui da lista todos os elementos da lista original


enquanto a condição especificada for verdadeira.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.dropWhile(x => x < 2)

novaLista: List[Int] = List(2, 3, 3)


Função: exists
Retorna “true” se a lista contém o elemento
especificado; retorna “false”, caso contrário.
scala> val lista = List(1,2,3,3)
scala> val existe = lista.exists (x => x == 1)
existe: Boolean = true
scala> val existe = lista.exists (x => x == 5)
existe: Boolean = false
Função: filter

Retorna todos os elementos que fazem a função


especificada retornar true.

scala> val lista = List(1,2,3,3)

scala> val novaListagem = lista.filter (x => x % 2 == 1)

novaListagem: List[Int] = List(1, 3, 3)


Função: flatten

A partir de uma lista de String, retorna uma lista de Char.

scala> val nomes = List("josé", "Luiz", "letícia")

nomes: List[String] = List(josé, Luiz, letícia)

scala> val novaLista = nomes.flatten

novaLista: List[Char] = List(j, o, s, é, L, u, i, z, l, e, t, í, c, i, a)


Outro exemplo:

scala> val listaDeListas = List(List(1,2,3), List(4,5,6))

listaDeListas: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))

scala> val listaSimples = listaDeListas.flatten

listaSimples: List[Int] = List(1, 2, 3, 4, 5, 6)


Função: forall
Testa se a função especificada retorna true para todos os
elementos.
scala> val lista = List(1,2,3,3)
scala> val ocorrencia = lista.forall (x => x != 2)
ocorrencia : Boolean = false
scala> val ocorrencia = lista.forall (x => x != 6)
ocorrencia : Boolean = true
Função: head

Retorna o primeiro elemento da lista

scala> val lista = List(1,2,3,3)

scala> val primeiroElemento = lista.head

primeiroElemento: Int = 1
Função: indexOf

Retorna a posição em que se encontra o elemento especificado.


Deve-se especificar, também, a posição inicial de busca (a primeira
posição tem índice 0). Se não encontrar, retorna “-1”.

scala> val lista = List(1,2,3,3)

scala> val posicao = lista.indexOf (2,0)

posicao: Int = 1

scala> val posicao = lista.indexOf (2,3)

posicao: Int = -1
Função: init

Retorna todos os elementos, com exceção do último


elemento.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.init

novaLista: List[Int] = List(1, 2, 3)


Função: intersect

Retorna o resultado da interseção entre duas listas.

scala> val lista = List(1,2,3,3)

scala> val outraLista = List(1,3,4)

outraLista: List[Int] = List(1, 3, 4)

scala> val novaListagem = lista.intersect(outraLista)

novaListagem: List[Int] = List(1, 3)


Função: isEmpty

Retorna “true” se a lista estiver vazia, e “false”, caso


contrário.

scala> val lista = List(1,2,3,3)

scala> val vazia = lista.isEmpty

vazia: Boolean = false


Função: last

Retorna o último elemento da lista.

scala> val lista = List(1,2,3,3)

scala> val ultimoElemento = lista.last

ultimoElemento: Int = 3
Função: lastIndexOf

Retorna a posição em que se encontra o elemento a partir da


posição especificada, do fim para o início. Caso não seja encontrado
o elemento, é retornado “-1”.

scala> val lista = List(1,2,3,3)

scala> val posição = lista.lastIndexOf(2,3)

posicao: Int = 1

scala> val posição = lista.lastIndexOf(2,0)

posicao: Int = -1
Função: map

Retorna uma nova lista, após a aplicação da função


especificada em todos os elementos da lista original.
scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.map ( x => x * 2 )

novaLista: List[Int] = List(2, 4, 6, 6)


scala> val nomes = List("josé", "Luiz", "letícia")

nomes: List[String] = List(josé, Luiz, letícia)

scala> nomes.map(_.toUpperCase)

res44: List[String] = List(JOSÉ, LUIZ, LETÍCIA)


Observação:

Função: flatMap

Executa a função map; em seguida, executa a função


flatten.

scala> nomes.flatMap(_.toUpperCase)

res45: List[Char] = List(J, O, S, É, L, U, I, Z, L, E, T, Í, C, I, A)


Função: max

Retorna o maior elemento da lista.

scala> val lista = List(1,2,3,3)

scala> val maiorElemento = lista.max

maiorElemento: Int = 3
Função: min

Retorna o menor elemento da lista.

scala> val lista = List(1,2,3,3)

scala> val menorElemento = lista.min

menorElemento: Int = 1
Função: mkString

Retorna uma string formada pelos elementos da


lista, separados pelo elemento separador
especificado. Pode ser especificado uma sequência
de escape como separador (por exemplo, a
tabulação: '\t').
scala> val lista = List(1,2,3,3)

scala> val str = lista.mkString(",")

str: String = 1,2,3,3

scala> val str = lista.mkString("\t")

str: String = 1 2 3 3

scala> val str = lista.mkString

str: String = 1233


Função: reverse

Retorna uma lista com os elementos em ordem invertida.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.reverse

novaLista: List[Int] = List(3, 3, 2, 1)


Função: sorted

Retorna uma lista ordenada.

scala> val listaNaoOrdenada = List(1,4,8,2,6,0,7,4,9)

listaNaoOrdenada: List[Int] = List(1, 4, 8, 2, 6, 0, 7, 4, 9)

scala> val listaOrdenada = listaNaoOrdenada.sorted

listaOrdenada: List[Int] = List(0, 1, 2, 4, 4, 6, 7, 8, 9)


Função: sum

Soma os elementos da lista.

scala> val lista = List(1,2,3,3)

scala> val soma = lista.sum

soma: Int = 9
Função: tail

Retorna todos os elementos da lista, com exceção do


primeiro elemento.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.tail

novaLista: List[Int] = List(2, 3, 3)


Função: take

Retorna os n primeiros elementos da lista.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.take(2)

novaLista: List[Int] = List(1, 2)


Função: takeRight

Retorna os últimos n elementos.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.takeRight(2)

novaLista: List[Int] = List(3, 3)


Função: toArray

Converte a lista em um array.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.toArray

novaLista: Array[Int] = Array(1, 2, 3, 3)


Função: toString

Converte a lista em uma string.

scala> val lista = List(1,2,3,3)

scala> val novaLista = lista.toString

novaLista: String = List(1, 2, 3, 3)

Você também pode gostar