Loading...
AnnaIvanova11 avatar AnnaIvanova11 21 Точки

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 4803 Точки
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 21 Точки

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

1
23/06/2020 02:26:00
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.