Buscar

Java: Comparação de Objetos

Prévia do material em texto

MC302
Primeiro semestre de 2017
Laboratório 6
Professores: Esther Colombini (esther@ic.unicamp.br) e Fábio Luiz Usberti (fusberti@ic.unicamp.br)
PEDs: (Turmas ABCD) Elisangela Santos (ra149781@students.ic.unicamp.br), Lucas Faloni
(lucasfaloni@gmail.com), Lucas David (lucasolivdavid@gmail.com), Wellington Moura
(wellington.tylon@hotmail.com)
PEDs (Turmas EF) Natanael Ramos (naelr8@gmail.com), Rafael Arakaki (rafaelkendyarakaki@gmail.com)
PAD: (Turmas ABCD) Igor Torrente (igortorrente@hotmail.com)
PAD: (Turmas EF) Bleno Claus (blenoclaus@gmail.com)
1 Objetivo
O objetivo deste laboratório será a familiarização com as outras coleções disponíveis no Java e a API
funcional disponível para streams implementada a partir do Java 8.
2 O operador equals e Object#equals
2.1 Introdução
A habilidade de comparar duas variáveis é essencial na programação de computadores. Na linguagem C,
o operador equals (i.e. ==) é frequentemente empregado a fim disto:
1 vo id s a y _ i f _ e q u a l s ( i n t a , i n t b ) {
2 i f ( a == b ) {
3 p r i n t f ( " a i s e q u a l t o b ! " ) ;
4 } e l s e {
5 p r i n t f ( " Not so l u c k y " ) ;
6 }
7 }
Listing 1: A comparação de dois inteiros na linguagem C.
A situação fica um pouco mais complexa ao comparar arrays, já que o valor contido em uma variável
do tipo int * é uma posição de memória e comparar duas posições de memória distintas sempre retorna
falso – ou 0 –, mesmo que os arrays contenha elementos idênticos.
2.1.1 O operador equals em Java
No Java, um “problema” similar ocorre em relação aos objetos. Assim como os arrays, os valores contidos
em variáveis – ou atributos – associadas à classes contém apenas referências à objetos daquelas classes.
A utilização do equals se limitaria, portanto, à comparação das referências e não dos dados propriamente
contidos nos objetos.
O código abaixo ilustra exemplos deste “problema”:
1 > I n t e g e r . va lueOf ( 1 0 ) == I n t e g e r . va lueOf ( 1 0 )
2 t r u e
3 > new I n t e g e r ( 1 0 ) == new I n t e g e r ( 1 0 )
4 f a l s e
1
5 > " H e l l o wor ld ! " == " H e l l o wor ld ! "
6 t r u e
7 > new S t r i n g ( " H e l l o wor ld ! " ) == new S t r i n g ( " H e l l o wor ld ! " )
8 f a l s e
9 > Ar ra ys . a s L i s t ( " Mariah " , " James " , " Smith " )
10 == Ar ra ys . a s L i s t ( " Mariah " , " James " , " Smith " )
11 f a l s e
Listing 2: Comparações entre objetos utilizando o operador equals.
Nota: é importante mencionar que as sentenças Integer .valueOf(10) == Integer .valueOf(10) e "Hello world!
" == "Hello world!" resultam em true pois existem mecanismos de cache ativos capazes de identificar que
objetos da classes String e Integer contendo os valores "Hello World!"e 10, respectivamente, foram
previamente construídos. Tais mecanismos, portanto, simplesmente retornam referências à estes objetos.
2.1.2 Object#equals(Object obj)
A fim de se comparar objetos de forma mais completa, utilizamos então o método Object#equals(Object obj).
Como definido em Object, dois objetos são iguais se as referências que os indicam são iguais. Isto é,
a. equals (b) é simplesmente um alias para (a == b). Este método pode, entretanto, ser sobrescrito de forma
similar ao método Object# toString () . No exemplo abaixo, duas pessoas são iguais se elas apresentam o
mesmo identificador e nome:
1 p u b l i c c l a s s P e r so n {
2 p r i v a t e i n t i d ;
3 p r i v a t e S t r i n g nome ;
4
5 p u b l i c P e r son ( i n t id , S t r i n g name ) {
6 t h i s . i d = i d ;
7 t h i s . name = name ;
8 }
9
10 @Override
11 p u b l i c b o o l e a n e q u a l s ( O b j e c t o b j ) {
12 i f ( t h i s == o b j ) r e t u r n t r u e ;
13 i f ( ! ( o b j i n s t a n c e o f P e r s on ) ) r e t u r n f a l s e ;
14 Pe r s on p = ( P e r s on ) o b j ;
15 i f ( name == n u l l | | p . name == n u l l ) r e t u r n f a l s e ;
16 r e t u r n i d == p . i d && name . e q u a l s ( p . name ) ;
17 }
18 }
Listing 3: Sobrescrita de Object#equals(Object obj) na classe Person.
Note que muitas classes já sobrescrevem Object#equals(Object obj), como Integer, String ou Abstrac-
tList:
1 > new I n t e g e r . va lueOf ( 1 0 ) . e q u a l s ( I n t e g e r . va lueOf ( 1 0 ) )
2 t r u e
3 > new I n t e g e r ( 1 0 ) . e q u a l s ( new I n t e g e r ( 1 0 ) )
4 t r u e
5 > " H e l l o wor ld ! " . e q u a l s ( " H e l l o wor ld ! " )
6 t r u e
7 > new S t r i n g ( " H e l l o wor ld ! " ) . e q u a l s ( new S t r i n g ( " H e l l o wor ld ! " ) )
8 t r u e
9 > Ar ra ys . a s L i s t ( " Mariah " , " James " , " Smith " ) . e q u a l s (
10 A rr ay s . a s L i s t ( " Mariah " , " James " , " Smith " ) )
11 t r u e
Listing 4: Comparações entre objetos utilizando o método equals.
2
Algumas classes já utilizam o Object#equals(Object obj) internamente:
1 > L i s t < S t r i n g > names = Ar ra y s . a s L i s t ( " Mariah " , " James " , " Smith " )
2 > names . c o n t a i n s ( new S t r i n g ( " James " ) )
3 t r u e
4 > names . indexOf ( new S t r i n g ( " James " ) )
5 1
6 > names . c o n t a i n s ( new S t r i n g ( " Kevin " ) )
7 f a l s e
8 > names . indexOf ( new S t r i n g ( " Kevin " ) )
9 −1
Listing 5: Exemplos de métodos em List<String> que utilizam String #equals(Object obj) internamente.
2.2 Atividade
1. Inicie um novo projeto Java chamado Lab6.
2. Reutilize as classes definidas no pacote base do laboratório 4 (e.g. Carta, Baralho) no projeto
Lab6.
3. Sobrescreva o método Object#equals(Object obj) na classe Carta, considerando seus atributos. Dica:
não é suficiente simplesmente comparar o nome das cartas, visto que um único baralho pode conter
mais de uma carta com o mesmo nome.
4. Sobrescreva o método Object#equals(Object obj) na classe Baralho, considerando as cartas presentes
e sua ordenação. Tente fazê-lo em uma única linha.
2.3 Tarefas
1. Crie um ArrayList de Cartas (ArrayList<Carta> cartas ) e adicione 10000 cartas à esta lista. Qual é a
soma do tempo necessário para buscar o elemento em cada posição i da lista utilizando List#get( int
i )?
2. Repita o experimento anterior utilizando uma LinkedList (LinkedList<Carta> cartas ) e reporte o tempo
necessário.
3. Novamente, crie um ArrayList (ArrayList<Carta> cartas ) de Cartas e adicione 10000 cartas à esta
lista. Qual é o tempo necessário para buscar todos os elementos o contidos na lista cartas através
do método List#contains (Object o)?
4. Repita o experimento anterior utilizando uma LinkedList (LinkedList<Carta> cartas ) e reporte o tempo
necessário.
5. Uma lista (ArrayList, LinkedList ou Vector) aceita uma carta repetida?
Dica: o tempo transcorrido pode ser calculado como descrito em Lst. 6.
1 l ong s = System . nanoTime ( ) ;
2
3 / / P r o c e s s a m e n t o . . .
4
5 System . o u t . p r i n t l n ( "A o p e r a c a o demorou " + ( System . nanoTime ( ) − s ) / 1000000 + " ms" ) ) ;
Listing 6: Medindo tempo transcorrido em um trecho de código.
3
3 Outras coleções, comparadores e Object#hashCode()
3.1 Introdução
Listas são coleções necessárias quando a ordem dos elementos ali contidos é um fator determinante na
solução do problema em mãos. A ordenação dos elementos de uma lista segue a ordem de inserção,
sendo independente de qualquer propriedade do objeto ali contido. List#contains (Object o) e List#indexOf(
Object o) devem, portanto, navegar por cada elemento el contido na lista e verificar o resultado da operação
el . equals (o); o que pode resultar em baixa performance em listas contendo imensa quantidade de objetos.
Quando manipulando grandes quantidades de objetos – e a ordem de inserção pouco importa –, outras
coleções (e.g. TreeSet, HashSet) podem ser mais adequadas.
3.1.1 HashSet
O HashSet é uma implementação de Set (que por sua vez implementa Collection) que utiliza interna-
mente uma tabela hash para guardar os objetos ali inseridos. Esta implementação é interessante quando a
quantidade e frequência de acesso à elementos inseridos são altas e ordenação destes não importa.
Como usualmente, uma tabela hash seutiliza de uma função hash h : T → N que mapeia objetos de
uma classe T à uma posição na tabela. Por padrão, a implementação da função hash utilizada é o método
Object#hashCode(); que pode ser sobrescrita da mesma forma que o método Object#equals(Object obj).
O exemplo abaixo descreve uma possível implementação para a função hash da classe Person, consi-
derando sua identidade e nome (os atributos considerados em Person#equals(Object obj)).
1 p u b l i c c l a s s P e r so n {
2
3 / / . . .
4
5 @Override
6 p u b l i c i n t hashCode ( ) {
7 i n t hash = 3 ;
8 hash = 67 * hash + i d ;
9 hash = 13 * hash + ( name != n u l l ? name . hashCode ( ) : 0 ) ;
10 r e t u r n hash ;
11 }
12 }
Listing 7: Uma possível implementação de função hash para a classe Person.
3.1.2 TreeSet
O TreeSet é uma implementação de Set (que por sua vez implementa Collection) que utiliza internamente
uma árvore rubro negra (i.e. um tipo de árvore binária auto balanceada). Essa estrutura apresenta uma
performance em acesso inferior ao HashSet, mas garante que os elementos estejam sempre ordenados de
acordo com um determinado comparador.
Dado a natureza das árvores binárias, onde objetos são colocados em nós à esquerda (menor) ou
direita (maior) de um objeto contido em um nó já existente, precisamos primeiramente descrever o que
significa para um objeto de uma classe ser maior, menor ou igual à um outro desta mesma classe. Isto
pode ser feito com a implementação de uma lambda function f que possui dois argumentos (os objetos a
e b que devem ser comparados) e retorna um valor negativo se a for menor que b, 0 se a for igual à b e
um valor positivo se a for maior que b.
Por exemplo, podemos criar um TreeSet de Persons indexados em ordem crescente por seus identi-
ficadores. As sentenças seguintes fazem exatamente isso e são equivalentes:
4
1 C o l l e c t i o n < Person > p e r s o n s = new TreeSe t < >(( Pe r s on p1 , Pe r s on p2 ) −> {
2 r e t u r n p1 . g e t I d ( ) − p2 . g e t I d ( ) ;
3 } ) ;
4 / / Ou . . .
5 C o l l e c t i o n < Person > p e r s o n s = new TreeSe t < >(( p1 , p2 ) −> {
6 r e t u r n p1 . g e t I d ( ) − p2 . g e t I d ( ) ;
7 } ) ;
8 / / Ou . . .
9 C o l l e c t i o n < Person > p e r s o n s = new TreeSe t < >(( p1 , p2 ) −> p1 . g e t I d ( ) − p2 . g e t I d ( ) ) ;
10 / / Ou . . .
11 C o l l e c t i o n < Person > p e r s o n s = new TreeSe t < >( Compara tor . c o m p a r i n g I n t ( P e r s on : : g e t I d ) ) ;
Listing 8: Um comparador de identidades de pessoas.
Finalmente, percorrer o conjunto persons com o for reduzido (for (Person p : persons) {}) resultaria em
um fluxo de objetos da classe Person ordenados por sua identidade.
3.2 Atividade
1. Sobrescreva o método Object#hashCode() na classe Carta, considerando seus atributos. Dica: algumas
classes já sobrescrevem seus próprios hashes (e.g. UUID#hashCode(), String #hashCode()).
2. Sobrescreva o método Object#hashCode() na classe Baralho. Tente fazê-lo em uma única linha.
3.3 Tarefas
1. Crie um HashSet de Cartas e adicione 10000 cartas à esta coleção. Qual é o tempo necessário
para buscar todos os elementos o contidos na lista cartas através do método List#contains (Object o)?
2. Repita o experimento anterior utilizando uma TreeSet de Cartas e reporte o tempo necessário.
Nota: o comparador empregado aqui deve utilizar os mesmos atributos considerados no método
Carta#equals(Object obj).
3. Um conjunto (HashSet, TreeSet) aceita uma carta repetida?
4 A API Stream
Todas as coleções implementam, a partir do Java 8, o método Collection #stream() , que retorna um Stream
(ou fluxo) de objetos contidos naquela coleção. O fluxo depende fundamentalmente de como a coleção
se organiza internamente, mas sempre pode ser utilizado para processar os dados ali contidos de forma
mais sucinta.
Seja people uma coleção de pessoas como definida abaixo:
1 C o l l e c t i o n < Person > p e o p l e = A r r ay s . a s L i s t (
2 new Pe r s on ( 0 , " John Reese " ) , new P er s on ( 1 , " K e l l y Sanches " ) ,
3 new Pe r s on ( 2 , " J e n n i f e r Walker " ) , new P er s on ( 3 , " John Creedence " ) ) ;
Listing 9: Lista people de objetos da classe Person.
5
Alguns exemplos de operações possíveis sobre streamings de people:
1 C o l l e c t i o n < Person > j o h n s = p e o p l e . s t r e a m ( )
2 / / F i l t r e o f l u x o , c a p t u r a n d o somente quem se chama John .
3 . f i l t e r ( p −> p . getName ( ) . s t a r t s W i t h ( " John " ) )
4 / / Ordene d e c r e s c e n t e m e n t e por seu i d .
5 . s o r t ( ( p1 , p2 ) −> p2 . g e t I d ( ) − p1 . g e t I d ( ) )
6 / / I g n o r e os d o i s p r i m e i r o s j o h n s .
7 . s k i p ( 2 )
8 / / L i m i t e o numero de e l e m e n t o s no f l u x o por 5 .
9 . l i m i t ( 5 )
10 / / C o n s t r u a uma l i s t a a p a r t i r do f l u x o .
11 . c o l l e c t ( C o l l e c t o r s . t o L i s t ( ) ) ;
Listing 10: Exemplo de filtragem, ordenação e limitação.
1 f i n a l i n t maxId = 2 ;
2 C o l l e c t i o n < Person > somePeople = p e o p l e . s t r e a m ( )
3 . f i l t e r ( p −> p . g e t I d ( ) < maxId )
4 . c o l l e c t ( C o l l e c t o r s . t o L i s t ( ) ) ;
Listing 11: Exemplo de filtragem com variáveis. Note que uma variável precisa ser final ou efetivamente
final para ser usada dentro da função lambda.
1 Pe r s on k e l l y = p e o p l e . s t r e a m ( )
2 / / F i l t r e o f l u x o , c a p t u r a n d o somente quem se chama K e l l y .
3 . f i l t e r ( p −> p . getName ( ) . s t a r t s W i t h ( " K e l l y " ) )
4 / / Pegue o p r i m e i r o o b j e t o .
5 . f i n d F i r s t ( )
6 / / Ou r e t o r n e n u l l s e nenhum e x i s t i r .
7 . o r E l s e ( n u l l ) ;
Listing 12: Exemplo de seleção.
1 C o l l e c t i o n < S t r i n g > peoplesNames = p e o p l e . s t r e a m ( )
2 / / Mapeie cada o b j e t o Pe r s on p a r a um o b j e t o S t r i n g .
3 . map ( p −> p . getName ( ) )
4 / / C o n s t r u a uma l i s t a a p a r t i r do f l u x o .
5 . c o l l e c t ( C o l l e c t o r s . t o L i s t ( ) ) ;
Listing 13: Exemplo de mapeamento.
1 i n t e g e r sumOfPeop le s Ids = p e o p l e . s t r e a m ( )
2 / / Mapeie cada o b j e t o Pe r s on p a r a um o b j e t o I n t e g e r
3 / / ( t r a n s f o r m a n d o Stream < Person > em um I n t S t r e a m ) .
4 . mapToInt ( p −> p . g e t I d ( ) )
5 / / Some t o d o s as i d e n t i d a d e s .
6 . sum ( ) ;
Listing 14: Exemplo de redução.
4.1 Atividades
1. Crie uma coleção com multiplos objetos das sub-classes de Carta.
2. Utilize o stream da coleção criada para selecionar o Lacaio de maior ataque.
3. Utilize o stream da coleção para calcular a soma de pontos de ataque dos lacaios presentes.
4. Utilize o stream da coleção para ordenar os lacaios por seus pontos de vida.
6
5 Submissão
Para submeter a atividade, utilize o Moodle (https://www.ggte.unicamp.br/ea). Crie um ar-
quivo texto com as respostas para cada item da seção tarefas e as saídas geradas pelo código. Compacte
o código-fonte contido no diretório src juntamente com arquivo de respostas no formato .zip ou similar
e nomeie-o Lab6-000000.zip, trocando ’000000’ pelo seu número de RA. Submeta o arquivo na seção
correspondente para esse laboratório no moodle da disciplina MC302.
Datas de entrega
• Dia 02/05 Turma ABCD até às 23:55h
• Dia 06/05 Turma EF até às 23:55h
7
https://www.ggte.unicamp.br/ea
	Objetivo
	O operador equals e Object#equals
	Introdução
	O operador equals em Java
	Object#equals(Object obj)
	Atividade
	Tarefas
	Outras coleções, comparadores e Object#hashCode()
	Introdução
	HashSet
	TreeSet
	Atividade
	Tarefas
	A API Stream
	Atividades
	Submissão

Continue navegando

Outros materiais