JayCesar / java-springboot

[📚 Study ] This repository was created to add files and my notes from the course POO of Java ( + spring);
2 stars 0 forks source link

Seção 19: Generics, Set, Map #47

Closed JayCesar closed 9 months ago

JayCesar commented 1 year ago

📍Generics

Generics permitem que classes, interfaces e métodos possam ser parametrizados por tipo, Seus benefícios são:

List<String> list = new ArrayList<>();
list.add("Maria");
String name = list.age(0);

2023-07-08_10h54_09


2023-07-08_10h55_48

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"

List<Object> list = new ArrayList<>();

Quando eu foi querer adicionar um dos valores dela para uma variável do tipo integer, eu terei que fazer um casting (converter):

Integer x = (Integer) ps.first(); // Eu vou ter que converter

Logo eu terei um problema de segurança de tipos → TypeSafety e também de Performance.

É aqui que entra os Generics: 2023-07-08_11h21_14

public class PrintService<T> {

    List<T> list = new ArrayList<>();

    public void addValue(T value) {
        list.add(value);
    }
}

⚠️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:

PrintService<Integer> ps = new PrintService<>();
// Repare que é preciso colocar '<>' no final antes dos parêntreses para dizer pro compilador que é um Generics;

// Seu quiser adicionar um tipo String eu não irei conseguir:

ps.addValue("Maria"); // O compilador irá reclamar

Classe Service:

// Agora minha classe está parametrizada com um certo tipo "T";
public class PrintService<T> {

    List<T> list = new ArrayList<>();

    public void addValue(T value) {
        list.add(value);
    }

    public T first() {
        if (list.isEmpty()) {
            throw new IllegalStateException("List is empty"); // Programação defensiva
        }
        return list.get(0);
    }

    public void print() {
        System.out.print("[");
        if (!list.isEmpty()) {
            System.out.print(list.get(0));
        }
        for(int i = 1; i < list.size(); i++) {
            System.out.print(", " + list.get(i));
        }
        System.out.println("]");
    }
}

Programa principal:

package application;

import java.util.Scanner;

import services.PrintService;

public class Program {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        PrintService<Integer> ps = new PrintService<>();

        System.out.println("How many values? ");
        int n = sc.nextInt();

        for (int i = 0; i < n; i++) {
            Integer value = sc.nextInt();
            ps.addValue(value);
        }

        ps.print();
        Integer x = ps.first(); 
        System.out.println("First: " + x);

        sc.close();

    }
}
JayCesar commented 1 year ago

Genéricos delimitados

2023-07-08_11h46_25

O jeito mais completo de implementação é esse:

2023-07-08_12h22_42

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

JayCesar commented 1 year ago

Tipos Curinga (Wildcard types)

List Não é supertipo de qualquer tipo de lista:

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;

2023-07-09_11h09_49


Com tipos curinga podemos fazer métodos que recebem um genérico de "qualquer tipo":

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

2023-07-09_11h15_29


JayCesar commented 1 year ago

📍Curingas delimitados (bounded wildcards)

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

Mas eu não consigo adicionar items à essa lista dentro desse método de iteração.


2023-07-10_17h21_46

Lembrando:

2023-07-10_17h25_01

2023-07-10_17h28_50

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!

Existe também o contrária: Contravariância:

2023-07-10_17h38_14

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!

  • Apenas acesso;

Quando eu trabalho com lista de de superclasse, isso é contravariância!

  • Apenas adiciono;

Meu código como anotações:

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();
    }
}
JayCesar commented 1 year ago

HashCode & equals

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!

Equals

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;

HashCode

É 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

2023-07-10_18h28_27

🧠 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:

image


HashCode e Equals personalizados

2023-07-10_18h33_15

Para comparar um cliente com o outro, isso vai depender do meu negócio, ou seja, do critério que eu irei abordar!

Rápido adendo:

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:

  • Retorna um valor negativo se o objeto atual for "menor" que o objeto passado como parâmetro.
  • Retorna zero se o objeto atual for "igual" ao objeto passado como parâmetro.
  • Retorna um valor positivo se o objeto atual for "maior" que o objeto passado como parâmetro.

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.

JayCesar commented 1 year ago

Set < T >

  • Representa um conjunto de elementos (similar ao da Álgebra);
    • Não admite repetições;
    • Elementos não possuem posição, existe somente ordem dependendo da implementação!
    • Acesso, inserção e remoção de elementos são rápidos;
    • Oferece operações eficientes de conjunto: interseção, união, diferença.
    • Principais implementações:
    • HashSet: Mais rápido (operações O(1) em tabela hash) e não ordenando; É tudo feito em ordem de "1", mas não garante a ordenação.
    • 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 adicionaods. 2023-07-11_07h09_57 2023-07-11_07h11_50

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!


Fazendo união, interseção e diferença com lista 'Set':

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);

    }

}
JayCesar commented 1 year ago

📌 Como TreeSet compara os elementos?

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;


JayCesar commented 1 year ago

Exercício resolvido (Set)

2023-07-25_06h52_25 2023-07-25_06h52_48


É 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]));
JayCesar commented 1 year ago

📌 Map <K, V>

É também uma estrutura de daods muito importante e também genérica, mas tem dois tipos:

  • Tipo Chave (k);
  • Tipo valor (V);

É uma coleção de pares chave / valor

  • Não admite repetições do objeto chave;
  • Os elementos são indexados pelo objeto chave (não pussuem posição);
  • Acesso, inserção e remoção de elementos são rápidos;

- Uso comum: cookies, local storage, qualquer modelo chave-valor;

Principais implementações:

  • HashMap - mas rápido (operações O(1) em tabela hash e não ordenado;
  • TreeMap - mais lento (operações O(log(n)) em árvore rubro negra) e ordenado pelo comparTo do objeto (ou Comparator);
  • LinkedHashMap - velocidade intermediária e elementos na ordem em que são adicionados

Alguns métodos importantens:

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
  • Baseado em equals e hashCode
  • Se equals e hashCode não existir, é usada a comparação de ponteiros
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)

2023-07-26_14h27_07

2023-07-26_14h27_10

Print do cookbook: image