Професионална програма
Loading...
+ Нов въпрос
AnnaIvanova11 avatar AnnaIvanova11 20 Точки

Regarding methods : equal() and hashCode() - Exercises: Iterators and Comparators.Problem 7 : *Equality Logic

Здравейте,

Моля за малко помощ, ако някой има възможност да отдели време.

Не мога да разбера, когато дам @Override на equals и използвам Set-ове, както е в тази задача*** // прилагам код и условие долу.

***Set - a преди да запише стойноста ми, вика equals, за проверка нали?

Моя презаписан equal ли използва? Искам да го дебъгна, но HashSet ме хвърля на hash метода, a TreeSet- a на comparator, как да го дебъгна без да ги тъпча в лист и да въртя iter преди добавяне в set-те. Накратко не ми е ясно как ги проверява и в какъв ред си извършват операциите. Промяната на hashCode, променя ли и как променя референцията? Благодаря предварително!

 

https://pastebin.com/UUbFT7Zt ---> код

https://pastebin.com/fpL76Qh5 ---> условие

Тагове:
1
Java Advanced 20/06/2020 23:30:10
MartinBG avatar MartinBG 2826 Точки
Best Answer

По дефиниция Set e "collection that contains no duplicate elements", т.е. в сета не може да има два еднакви елемента. За прости типове като стрингове или числа е лесно да се дефинира условието за еднаквост (и то е част от езика), но когато става дума за обекти, това е работа на програмиста.

 

В Java има няколко имплементации на Set, като най-често използваните са TreeSet и HashSet.

 

При HashSet вътрешната подредбата на елементите се определя от техния hash код, правилата за изчислението на който са дефинирани в hashCode() метода, а уникалността им се валидира чрез equals() метода.

Ако не презапишем тези методи в нашия клас, се използват наследените от Object класа, които от своя страна използват адреса в паметта, на който е записан обекта.

Ето една статия по темата. 

При опит за добавяне на нов обект в HashSet първо се изчислява hash-a му чрeз извикване на hashCode() метода, а после се проверява дали вече няма обект със същия hash, за който equals() метода да върне true.

 

В TreeSet-a елементите се подреждат по големина спрямо критериите, зададени в Comparable<T>#compareTo(T o) метода.

При опит за добавяне на нов обект в TreeSet се проверява дали вече няма обект, за който compareTo(T o) метода спрямо новият обект да върне 0.

 

Ако променим поле на обект, който вече е в HashSet или TreeSet, и това поле се използва от hashCode(), equals() или compareTo(T o) методите, вътрешната подредба и съдържание на тези сетове вече няма да отговаря на контракта им, и от това може да възникнат най-различни бъгове. Ако подобна промяна се налага, редно е първо да извадим обекта от сета и да го добавим отново след модификацията му.

 

Надявам се, че съм успял да внеса малко яснота по темата, а ето и демо, с което може да експериментирате:

 

import java.util.*;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        Person theSetContractBreaker = new Person("Ivan", 22);

        List<Person> people = List.of(
                new Person("Ivan", 17),
                new Person("ivan", 17),
                new Person("Stoqn", 25),
                new Person("Ivan", 18),
                new Person("Ivan", 17),
                new Person("Stopn", 25),
                new Person("Stoqn", 25),
                theSetContractBreaker
        );

        Set<Person> treeSet = new TreeSet<>(people);
        Set<Person> hashSet = new HashSet<>(people);

        System.out.println(treeSet.size() + ": " + treeSet.stream()
                .map(Person::toString).collect(Collectors.joining(", ")));
        System.out.println(hashSet.size() + ": " + hashSet.stream()
                .map(Person::toString).collect(Collectors.joining(", ")));

//        Uncomment following lines of code to break set contract:
//        there will be 2 "Ivan 17" objects in each set
/*
        System.out.println("Changing \"Ivan 22\" to \"Ivan 17\"");

        theSetContractBreaker.age = 17;

        System.out.println(treeSet.size() + ": " + treeSet.stream()
                .map(Person::toString).collect(Collectors.joining(", ")));
        System.out.println(hashSet.size() + ": " + hashSet.stream()
                .map(Person::toString).collect(Collectors.joining(", ")));
*/
    }

    private static final class Person implements Comparable<Person> {
        private final String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return name + " " + age;
        }

        @Override
        public boolean equals(Object o) {
//            return true; // all objects with the same hash are considered the same (in HashSet)
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Person person = (Person) o;
            return age == person.age && Objects.equals(name, person.name);
        }

        @Override
        public int hashCode() {
//            return 1; // All objects have the same hash (in HashSet)
            return Objects.hash(name, age);
        }

        @Override
        public int compareTo(Person person) {
//            return 0; // all objects are considered equal for ordering (in TreeSet)
            int cmp = name.compareTo(person.name);
            return cmp != 0
                    ? cmp
                    : Integer.compare(age, person.age);
        }
    }
}

 

1
21/06/2020 06:36:30
AnnaIvanova11 avatar AnnaIvanova11 20 Точки

@MartinBG Благодаря много за изчерпателния пост!!! Колкто повече уж научавам имам повече въпроси и по - малко знам хаха. Признателна съм, така както си го обяснил  и с примера разбрах.

1
23/06/2020 02:26:00