Closed JayCesar closed 9 months ago
O jeito mais completo de implementação é esse:
public static <T extends Comparable<? super T>> T max(List<T> list)
É um tipo comparável T, ou qualquer super classe de T! Pode ser o Produto ou qualquer Super classe do Produto! Porque se alguma super classe do Produto possuir o CompareTo, o produto irá herdar esse compareTo
List
List<Object> // Não é supertipo de qualquer tipo de lista:
List<Object> myObjs = new ArrayList<Object>()
List<Integer> myNumbers = new ArrayList<Integer>();
myObjs = myNumbers; // erro de compilação
List de Integer não é uma Lista de Object!
O supertipo de qualquer tipo de lista é List<?>. Este é um tipo curinga:
List<?> myObjs = new ArrayList<Object>();
List<Integer> myNumbers = new ArrayList<Integer>();
myObjs = myNumbers;
package application;
import java.util.Arrays;
import java.util.List;
public class ProgramWildCardsTest {
public static void main(String[] args) {
List<Integer> myInts = Arrays.asList(5, 2, 10);
printList(myInts);
List<String> myStrings = Arrays.asList("Teste", "Teste2");
printList(myStrings);
}
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
}
⚠️ Porém não é possível adicionar dados a uma coleção de tipo curinga
Eu consigo em um método, passar como argumento uma lista que tem um tipo que eu nao sei "Curinga" e também dizer que essa lista extends uma classe / interface!
Aqui:
public static double totalArea(List<? extends Shape> list)
Todo o código:
package application;
import java.util.ArrayList;
import java.util.List;
import entities.Circle;
import entities.Rectangle;
import entities.Shape;
public class ProgramBoundedWildCard {
public static void main(String[] args) {
List<Shape> myShapes = new ArrayList<>();
myShapes.add(new Rectangle(3.0, 2.0));
myShapes.add(new Circle(2.0));
List<Circle> myCircles = new ArrayList<>();
myCircles.add(new Circle(2.0));
myCircles.add(new Circle (3.0));
System.out.println("Total area: " + totalArea(myCircles));
}
public static double totalArea(List<? extends Shape> list) {
// A minha lista pode ser de Shape ou de qualquer tipo que possa ser de subtipo de Shape;
double sum = 0.0;
for (Shape s : list) {
sum += s.area();
}
return sum;
}
}
Mas eu não consigo adicionar items à essa lista dentro desse método de iteração.
Isso significa que eu eu consigo criar uma variável list de algum tipo genérico que vai receber essa List de Integer (desde que essa variável list seja subclasse da number). Mas eu não posso adicionar um item à essa lista pois eu não sei de qual tipo será ela.
Isso se chama Covariãncia: É quando a operaçã de get() é permitida, mas a operaçãode inserir não é permitida!
Ou seja, quando eu consigo adicionar elemenos mas não consigo acessar eles!
Essa lista:
List<? super Number> myNums = myObjs;
O '?' pode ser tanto Number
quanto Object
.
Ou seja, quando eu for atribuir um valor à variável do tipo Number, pode ser que o valor de myNumbs.get(0) não seja um número, porque a lista myNums pode ser do tipo Object, ou seja, podendo ter outros tipos para além de números, tais como String.
Quando eu trabalho com lista de subtipos, isso é uma covariância!
Quando eu trabalho com lista de de superclasse, isso é contravariância!
public class ProgramCopyingLists {
public static void main(String[] args) {
List<Integer> myInts = Arrays.asList(1, 2, 3, 4);
List<Double> myDoubles = Arrays.asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
printList(myObjs);
copy(myDoubles, myObjs);
printList(myObjs);
}
// Esse método receberá uma lista 'source' que seja subtipo de número
// E esse método terá também uma lista 'destiny' que pode ser super classde Number, ou seja, Object.
public static void copy(List<? extends Number> source, List<? super Number> destiny) {
for(Number number : source) {
destiny.add(number);
}
}
public static void printList(List<?> list) {
for(Object obj : list) {
System.out.print(obj + ", ");
}
System.out.println();
}
}
São duas operações!
Elas são operações da classe Object utilizadas para comparar se um objeto é igual a outro!
equals: ela é uma operação lenta, porém a resposta é 100%;
hashcode: é rápido, porém a resposta positiva não é 100%,
Tipos comuns (String, Date, Integer, Double, etc) já possuem implementação para essas operações. Classes personalizadas precisam sobrepõ-las!
Método que compara se o objeto é igual a outro, retornando true ou false;
String a = "Maria";
String b = "Alex";
System.out.println(a.equals(b)); // Retornará false;
É um Método que retorna um número inteiro representando um códgio gerado a partir das informações do objeto.
String a = "Maria";
String b = "Alex";
System.out.println(a.hashCode(b));
System.out.println(b.hashCode(b));
O HashCode é um código gerador a partir de uma função de geração da classe que irá produzir um número inteiro. Existem inúmeros formas de gerar um numéro Hash a partir de dados. A forma específica não importa, o que é importante é que para dados do mesmo conteúdo, precisa ser gerado o mesmo código! Com dados diferentes muito provavelmente será um código diferente! Mas pode haver que conincidentemente o código Hash de dois objetos diferentes sejam iguais (mas é MUITO difícil)!
O HashCode é bom para testar uma massa muito grante de objetos que eu sei que a maioria é difernete entre sí! Se eu te
🧠 Conclusão: Embora haja uma possibilidade de uma colisão de dois objetos diferentes gerarem o mesmo HashCode, o HashCode existe mesmo assim porque ele é muito rápido de ser calculado! Exemplo: Se eu tiver que procurar um objeto numa lista de 1 Bilião de elementos: 1) Eu percorro a lista comparando meu objeto com o elemento da lista usando o HashCode; 2) Se em algum momento o HashCode for igual, eu confirmo a iguadle com o método Equals
Lembrando:
Para comparar um cliente com o outro, isso vai depender do meu negócio, ou seja, do critério que eu irei abordar!
O método compareTo
em Java é usado para comparar dois objetos do mesmo tipo. Ele retorna um valor inteiro que indica a relação de ordem entre os objetos. A assinatura do método é a seguinte:
int compareTo(T objeto)
Onde T
é o tipo do objeto a ser comparado. O valor de retorno pode ter os seguintes significados:
O critério de comparação é definido pela implementação do método compareTo
na classe do objeto. Por exemplo, se você tiver uma classe Pessoa
com um atributo idade
, pode implementar o método compareTo
para comparar as idades das pessoas.
O HashSet<> não garante a ordem! Ou seja, é indicado quando a ordem não importa! O LinkedHashSet<> mantém a ordem em que os elementos foram inseridos!
package application;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
public class ProgramSetDemo2 {
public static void main(String[] args) {
Set<Integer> a = new TreeSet<>(Arrays.asList(0, 2, 4, 5, 6, 8, 10));
Set<Integer> b = new TreeSet<>(Arrays.asList(5, 6, 7, 8, 9, 10));
// union
Set<Integer> c = new TreeSet<>(a);
// Aqui eu estou fazendo um cópia do set 'a' para o set 'b'
c.addAll(b);
// Depois eu faço uma união
System.out.println(c);
// intersection
// Junta somente os elementos que eles têm em comum
Set<Integer> d = new TreeSet<>(a);
d.retainAll(b);
System.out.println(d);
// difference
Set<Integer> e = new TreeSet<>(a);
e.removeAll(b);
// Vou remover do conjunto 'e' todo mundo que tá no conjunto b
System.out.println(e);
}
}
Recordando as implementações:
- HashSet - mais rápido (operações O91) em tabela hash) e não ordenado;
- TreeSet - mais lento (operações O(log(n)) em árvore rubro-negra) e ordenado pelo compareTo do objeto (ou Comparator);
- LinkedHashSet - velocidade intermediária e elementos na ordem em que são adicionados;
É importnate utilizar o Set aqui porque nele não há repetições, logo faz sentido nesse contexto de logs.
Obs: Ele pegou a data dessa forma:
Date moment = Date.from(Instant.parse(fields[1]));
É também uma estrutura de daods muito importante e também genérica, mas tem dois tipos:
É uma coleção de pares chave / valor
- Uso comum: cookies, local storage, qualquer modelo chave-valor;
Principais implementações:
put(key, value) // insere um valor / elemento numa determianda chave
remove(key) // passa a chave
contains(key) // verifica se existe esse chave
get(key) // recuperar o elemento pela chave
clear();
size()
keySet() // retorna um Set<k> (um Set com as chaves contidas no Map)
values() // retorna um Collection<V> (uma coleção do tipo valor)
Print do cookbook:
📍Generics
Generics permitem que classes, interfaces e métodos possam ser parametrizados por tipo, Seus benefícios são:
Reuso
Type safety;
Performance;
Uso comum: coleções
Supomos que ao invés de números, eu queira utilizar nomes! ou seja, "Strings", logo não seria possível!
Seu deixar a List com o tipo "Object"
Quando eu foi querer adicionar um dos valores dela para uma variável do tipo integer, eu terei que fazer um casting (converter):
Logo eu terei um problema de segurança de tipos → TypeSafety e também de Performance.
É aqui que entra os Generics:
⚠️Se eu instanciar um PrintService do tipo String, ele só irá aceitar String, se for Inteiro, será só Inteiro e etc..
Quando eu for instanciar no programa principal, eu preciso indicar o tipo:
Classe Service:
Programa principal: