How does HashSet "contains" method work? - java

i use 2 implementations of Set:HashSet and TreeSet.I added 10 elements to set and find object by contains method from set.I see that contains method iterate all of the objects although it found element.For performance reason i confused.Why it is so and how can i prevent it?
I have a Person class:
public class Person implements Comparable<Person>{
private int id;
private String name;
public Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
//getter and setters
#Override
public int hashCode() {
System.out.println("hashcode:" + toString());
return this.id;
}
#Override
public boolean equals(Object obj) {
System.out.println("equals:" + toString());
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Person other = (Person) obj;
return true;
}
#Override
public String toString() {
return "Person{" + "id=" + id + ", name=" + name + '}';
}
#Override
public int compareTo(Person o) {
System.out.println("compare to:"+getId()+" "+o.getId());
if(o.getId() == getId()){
return 0;
}else if(o.getId()>getId()){
return -1;
}else {
return 1;
}
}
}
And in main class i add 10 Person object and then call contains method by first element of set:
import beans.Person;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Person> people = new HashSet<>();
for (int i = 0; i < 10; i++) {
people.add(new Person(i, String.valueOf(i)));
}
Person find = people.iterator().next();
if (people.contains(find)) {
System.out.println("here"+find.getName());
}
}
}
And result:
hashcode:Person{id=0, name=0} <--here element has been found but it continues
hashcode:Person{id=1, name=1}
hashcode:Person{id=2, name=2}
hashcode:Person{id=3, name=3}
hashcode:Person{id=4, name=4}
hashcode:Person{id=5, name=5}
hashcode:Person{id=6, name=6}
hashcode:Person{id=7, name=7}
hashcode:Person{id=8, name=8}
hashcode:Person{id=9, name=9}
hashcode:Person{id=0, name=0}<-- second check
here:0

Your equals() method is wrong. It returns true whatever the other person is.
It doesn't respect the contract of equals(), BTW, since equal objects should have an equal hashCode, and the hashCode is the ID of the person. So two persons with different IDs have a different hashCode, but are still equal.
That said, your test shows that hashCode() is executed 10 times. But it's not executed by contains(). It's executed by add(). Every time you add an object to the set, its hashCode() is used to know which bucket should hold the object.

It doesn't iterate though all elements. The reason the hashcode is bring printed for each object is because the hashcode needs to be calculated when Inserting into the set.

The first 10 prints are perhaps for the insert code and last one for the contains call. I hope that is the thing which might be confusing you.

Related

Java arraylist of objects how to search for elements error

I am trying to Implement a class named Parade using an ArrayList, which will manage instances of class Clown. Each Clown needs to be identified by all object data String for their name, int id and double size. I join a new Clown to the end of the Parade. Only the Clown at the head of the Parade (i.e., the first one) can leave the Parade. In addition, I write a method called isFront that takes a Clown as parameter and returns true if the passed clown is at the front of the parade otherwise returns false. Create a test application to demonstrate building a parade of three or four clowns and include your own name. Then, remove one or two, and add another one or two. Also, test the isFront method by passing different clowns to the method.
I have a code but it is not returning true for the isFront method, I am trying to use contains method I also tried to use Comparable interface Clown but it did not work that well. Not sure what to do.
import java.util.ArrayList;
public class Main
{
public static void main(String[] args)
{
Parade circus = new Parade();
circus.addClown(new Clown("Bobby",9,12.0));
circus.addClown(new Clown("Clair", 2, 11.0));
circus.addClown(new Clown("Tony",6,10.0));
circus.addClown(new Clown("Sarah",3,5.0));
circus.display();
System.out.println(circus.isFront(new Clown("Bobby", 9, 12.0)));
}
}
import java.util.ArrayList;
public class Parade
{
private static ArrayList<Clown> parade;
private int top;
public Parade()
{
top=0;
parade= new ArrayList<Clown>();
System.out.println(parade);
}
public void addClown(Clown c)
{
parade.add(c);
top++;
}
public void removeClown() //(Clown c)
{
parade.remove(0);
top--;
}
public void display()
{
System.out.println(parade);
}
public void getData()
{
parade.get(0);
}
public void setData()
{
parade.set(1,new Clown("Claire",2,5.0));
System.out.println(parade);
}
public int getTop()
{
return top;
}
public boolean isFront(Clown c)
{
return !parade.isEmpty() && c.equals(parade.get(0));
}
//exceptions
}
public class Clown
{
private String name;
private int id;
private double size;
public Clown(String name, int id, double size)
{
this.name=name;
this.id=id;
this.size=size;
}
public String getName()
{
return name;
}
public int getId()
{
return id;
}
public double getSize()
{
return size;
}
public String toString()
{
return name.toString() + id + size;
}
public boolean equals(Object o) {
if (o instanceof Clown c) {
return this.getName().equals(c.getName()) && this.getId() == c.getId() && this.getSize() == c.getSize();
}
return false;
}
}
their is not much info in our textbook about this stuff Java FOundations 5th e Lewis like working with objects and arraylists it skips it and assumes you already know it lol..
Firstly, objects in Java are, by default, compared by reference. So, even if you create two Clown objects with the exact same properties, Java sees them as different objects because both those object references are not the same, they are both referring to different memory locations. You can override this behavior and ask Java to compare it as you want by overriding the equals() method of the Object class:
public class Clown {
private String name;
private int id;
private double size;
public Clown(String name, int id, double size) {
this.name=name;
this.id=id;
this.size=size;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public double getSize() {
return size;
}
#Override
public boolean equals(Object o) {
if (o instanceof Clown) {
Clown c = (Clown) o;
return this.getName().equals(c.getName());
}
return false;
}
#Override
public int hashCode() {
return this.getId();
}
#Override
public String toString() {
return name.toString() + id + size;
}
}
This will help with contains()(it internally uses equals()).
Secondly, you can just compare your clown with the first clown to see if it is the one at the front:
public boolean isFront(Clown c) {
return !parade.isEmpty() && c.equals(parade.get(0));
}
The isFront() method will return true if the parade is not empty and the clown c is equal to to the first clown in the parade. get(0) retrieves the first clown in the parade.
As per your comment, if you want that two clowns be equal only if all their properties are equal, change your equals method to:
#Override
public boolean equals(Object o) {
if (o instanceof Clown) {
Clown c = (Clown) o;
return this.getName().equals(c.getName()) &&
this.getId() == c.getId() &&
this.getSize() == c.getSize();
}
return false;
}
The equals() method is of the Object class which is the parent class of all Java classes. It defines how to compare two objects.
Its signature is as follows:
public boolean equals(Object obj)
As we're overriding, its signature must be the same in the derived class, in our case in class Clown. Its parameter is of type Object not Clown. Any type can be converted to Object, if I compare an object of Clown to another type, like:
Clown c = new Clown("X", 1, 10);
if ( c.equals(objectOfAnotherType) ) {..}
it will still work.
So we use the instanceof operator to check if that another object is also a Clown. If it is not an instance of Clown, we return false but if it is, we convert/cast that object to Clown, only then we can call getName() and other getter methods:
if (o instanceof Clown) {
Clown c = (Clown) o; //Casting happens here
return this.getName().equals(c.getName()) &&
this.getId() == c.getId() &&
this.getSize() == c.getSize();
}
return false;
Java 14 introduced a shortcut for this, instead of these steps:
if (o instanceof Clown) {
Clown c = (Clown) o;
we can simply write:
if (o instance of Clown c)
which does the casting for us and stores it in c.
Lastly, I have also overriden Object.hashCode() because you have to when you override equals(), here's why.

In Collections, how can i get the index using the indexOf method in the following example

class Fruit{
public String name;
Fruit(String name){
this.name = name;
}
}//end of Fruit
class FruitList{
public static void main(String [] arg5){
List<Fruit> myFruitList = new ArrayList<Fruit>();
Fruit banana = new Fruit("Banana");
//I know how to get the index of this banana
System.out.println("banana's index "+myFruitList.indexOf(banana));
//But i'm not sure how can i get the indices for the following objects
myFruitList.add(new Fruit("peach"));
myFruitList.add(new Fruit("orange"));
myFruitList.add(new Fruit("grapes"));
}//end of main
}//end of FruitList
Since the rest of the objects that i've added to the ArrayList have no references, i'm not quite sure how their index can be retrieved. Please help, Thanks so much.
It does not matter which reference the object has if you redefine the equals and hashcode methods in the Fruit class. indexOf, contains, etc use the equals(...) method to decide if the object exists inside the collection.
For example, your Fruit class, could be like this (I changed your public String name to private):
public class Fruit {
private String name;
public Fruit(String name){
this.name = name;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + Objects.hashCode(this.name);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Fruit other = (Fruit) obj;
if (!Objects.equals(this.name, other.name)) {
return false;
}
return true;
}
Then:
Fruit f = new Fruit("orange");
myFruitList.indexOf(f); // this should return the orange fruit index (would be 1 in your example).

Get Common Element in Two Lists of Book

I have two array lists that store instances of a class called Book. I am trying to get the book/books that is inside both lists.
This is a search feature that allows you to search for a book by entering the book's ISBN, Name and Author. The list 'resultA' contains the books with the inputted ISBN and Name while the other list 'resultB' contains the books written by the inputted author. To get the final result I need to get the book that is inside both arrays.
I have tried using the retainAll() function but I found that it doesn't work on lists with instances stored.
List<Book> resultA = BookManager.getBooksWhere("book_ISBN", ISBN, "book_name", bookName);
List<Book> resultB = BookManager.getBooksByAuthors(authors);
resultB.retainAll(resultA);
searchResults = resultA;
Is there some other function I can use instead to get the common book?
(Update)
Sorry, Here is the Book class:
public class Book
{
private int bookID;
private String bookISBN;
private String category;
private int libId;
private String name;
#Override
public String toString()
{
String output = bookISBN + " - " + name + " - " + category + " - ";
return output;
}
public int getBookID()
{
return bookID;
}
public void setBookID(int bookID)
{
this.bookID = bookID;
}
public String getBookISBN()
{
return bookISBN;
}
public void setBookISBN(String bookISBN)
{
this.bookISBN = bookISBN;
}
public int getLibId()
{
return libId;
}
public void setLibId(int libId)
{
this.libId = libId;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
(Update)
I did not know that I had to override the Books class for this to work and thanks for pointing that out DNA and pbabcdefp. I have looked up on how to do it and it has worked correctly, the common book is being taken out from both lists.
This was inserted in the book class and uses their unique id to compare if books are equal.
#Override
public boolean equals(Object o)
{
if (o == null)
return false;
if (getClass() != o.getClass())
return false;
final Book otherBook = (Book) o;
if (this.bookId != otherBook.bookId)
{
return false;
}
return true;
}
Assuming you defined an equals function for the Book class, here is a function that can get the common elements in two arrays:
public static <T> List<T> getCommonElements(List<T> list1, List<T> list2) {
List<T> resultsList = new ArrayList<>();
for (T element1: list1) {
for (T element2: list2) {
if (element1.equals(element2)) {
resultsList.add(element2);
}
}
}
return resultsList;
}
This looks like a school question. With that, I doubt you are looking for an answer with generics or comparators or overriding the compareTo or equal method.
Hence, this is what you can do:
for(int x=0; x<listA.size(); x++)
for(int y=0; y<listB.size(); y++)
if(listA.get(x).getISBN().equals(listB.get(y).getISBN()))
return listA.get(x);
Instead of using equals to compare, you get use their ISBN which is supposed to be their unique id. Alternatively, you can override the equals method within the Book class to compare the ISBN.

sorting collection wrt primitive value

suppose, I have a student class with roll number and name. I want to sort it out wrt roll number. I tried the following .Here is my code:
package CollectionDemo;
import java.util.*;
class student1 implements Comparable<student1>{
int rollNo;
String name;
student1(int rollNo,String name){
this.rollNo=rollNo;
this.name=name;
}
#Override
public boolean equals(Object o){
if((o instanceof student1) && (((student1)o).rollNo == rollNo)){
return true;
}
else
{
return false;
}
}
#Override
public int hashCode(){
return 1;
}
public int compareTo(student1 s) {
return s.rollNo;
}
public String toString(){
return "["+rollNo+","+name+"]";
}
}
public class treeSetDemo {
public static void main(String... a){
Set<student1> set=new TreeSet<student1>();
set.add(new student1(102,"Anu"));
set.add(new student1(101,"Tanu"));
set.add(new student1(103,"Minu"));
System.out.println("elements:"+set);
}
}
o/p: elements:[[102,Anu], [101,Tanu], [103,Minu]]
so, its not sorting:( how to make it correct .
thanks for your help.
================================================
thanks for all your help. The following code runs fine, but now I want to know how it works, if i comment out equals and hashcode method.
package CollectionDemo;
import java.util.*;
class student1 implements Comparable<student1>{
int rollNo;
String name;
student1(int rollNo,String name){
this.rollNo=rollNo;
this.name=name;
}
/* #Override
public boolean equals(Object o){
if((o instanceof student1) && (((student1)o).rollNo == rollNo)){
return true;
}
else
{
return false;
}
}
#Override
public int hashCode(){
return 1;
}
*/
public int compareTo(student1 s) {
System.out.println("hello:"+(this.rollNo-s.rollNo));
return this.rollNo-s.rollNo;
}
public String toString(){
return "["+rollNo+","+name+"]";
}
}
public class treeSetDemo {
public static void main(String... a){
Set<student1> set=new TreeSet<student1>();
set.add(new student1(102,"Anu"));
set.add(new student1(101,"Tanu"));
set.add(new student1(103,"Minu"));
System.out.println("elements:"+set);
}
}
OP:
run:
hello:-1
hello:1
elements:[[101,Tanu], [102,Anu], [103,Minu]]
BUILD SUCCESSFUL (total time: 0 seconds)
you have to change compareTo method in bellow way
public int compareTo(student1 s) {
if(s.rollNo == this.rollNo){
return 0;
}else if(s.rollNo > this.rollNo){
return -1;
}else{
return 1;
}
}
- If you want to sort on the basis of only one attribute, then go with java.lang.Comparable<T> Intereface, along with Collections.sort(List l).
- But if you aim is to sort it on the basis of more then one attribute then go for java.util.Comparator<T> along with Collections.sort(List l, Comparator c).
Eg:
import java.util.Comparator;
public class Fruit implements Comparable<Fruit>{
private String fruitName;
private String fruitDesc;
private int quantity;
public Fruit(String fruitName, String fruitDesc, int quantity) {
super();
this.fruitName = fruitName;
this.fruitDesc = fruitDesc;
this.quantity = quantity;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getFruitDesc() {
return fruitDesc;
}
public void setFruitDesc(String fruitDesc) {
this.fruitDesc = fruitDesc;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int compareTo(Fruit compareFruit) {
int compareQuantity = ((Fruit) compareFruit).getQuantity();
//ascending order
return this.quantity - compareQuantity;
//descending order
//return compareQuantity - this.quantity;
}
public static Comparator<Fruit> FruitNameComparator
= new Comparator<Fruit>() {
public int compare(Fruit fruit1, Fruit fruit2) {
String fruitName1 = fruit1.getFruitName().toUpperCase();
String fruitName2 = fruit2.getFruitName().toUpperCase();
//ascending order
return fruitName1.compareTo(fruitName2);
//descending order
//return fruitName2.compareTo(fruitName1);
}
};
}
I think this implementation is close to recommended:
#Override
public int compareTo(Object other) {
if(other == null || !(other instanceOf student)){
throw new IllegalArgumentException();
}
student s = (student) other;
if(this.rollNo > s.rollNo){
return 1;
} else if (this.rollNo < s.rollNo){
return -1;
} else {
return 0;
}
}
If you are using Comparable interface then your compareTo() method should return the comparison not equals method , Google comparable example.
Check this link
In your compareTo method, you are just returning the value of the object you are comparing to. You need to return the difference, of the attribute of the invoking instance and passed instance.
So, change your compareTo method to the below one: -
#Override
public int compareTo(student1 s) {
return this.rollNo - s.rollNo;
}
NOTE: - Only sign is important for Collections.sort, so you don't really need an if-else block to return -1, 0, or 1. Just return the difference. That's all.
P.S : -
Your hashcode implementation is a very poor one. It will put every instances in the same bucket.
#Override
public int hashCode(){
return 1; // All the instances will have the same hashcode.
}
Ideally, you should use only those attributes to calculate the hashCode which you have used to compare your two instances, here its rollNo.
So, rather than returning simply a value 1, you can have some equations, that calculates your hashcode, taking into to consideration your rollNo and a large prime number also.
You can go through Effective Java - Item#9 for more explanation of this topic.
Now, that your code is working fine, lets move to your 2nd doubt.
equals and hashCode methods are not used when you want to compare two objects that will be used while sorting. We override equals and hashCode methods in order to check whether an instance is equal to another instance later on.
So, compareTo method is not concerned with whether you have ocerrided equals ad hashCode method or not. And you can also infer from name as to what the two methods does, and can they be related or not.
Moreover, equals method is defined in Object class, whereas compareTo method is declared in Comparable interface. So, they are not interrelated.
Check the documentation of these methods: - Object#equals, Object#hashCode, and Comparable#compareTo

How can I override equals() method using inheritance?

I have a subclass called "worker" extending the "Person" class. I am trying to override the equals() method from "Person" within the subclass of "Worker". Can anyone explain if my attempt is correct in terms of a basic override?
public class Person {
private String name;
public Person(String n) {
name = n;
}
public Person() {
this("");
}
public String getName() {
return name;
}
public String toString() {
return getName() + " ";
}
public boolean equals(Object rhs) {
if (!(rhs instanceof Person)) {
return false;
}
Person other = (Person) rhs;
return this.getName().equals(other.getName());
}
class Employee extends Person {
double salary;
public Employee(double b) {
salary = b;
}
Employee() {
salary = 150000;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return super.toString();
}
// my attempt
#Override
public boolean equals(Object rhs) {
if (rhs == null) {
return false;
}
if (getClass() != rhs.getClass()) {
return false;
}
if (!super.equals(rhs))
return false;
else {
}
return false;
}
}
}
You've sort of mechanically handled the override part correctly, but the equals method of your Employee class will never return true.
First because of this:
if (!super.equals(rhs)) return false; else { }
You'll always fall through to the final return false, even if the result of super.equals is true.
Once you fix that, you still have a problem with the equals method of Person. If you pass in an instance of Worker, that equals method will always return false.
There may yet be other things, but those two are show stoppers.
The NetBeans using the shortcut alt + insert, you can automatically generate the equals method, constructors, getters, setters, delegate method, and others. If you want to use the collection using encoding mixing(Hashtable, HashMap, HashSet) it with redefining equals you must also redefine the hashCode().

Categories

Resources