Trying to override the hashcode()

  • Thread starter Thread starter STEMucator
  • Start date Start date
AI Thread Summary
The discussion focuses on overriding the hashCode(), equals(), and compareTo() methods in a Java class called SongEntry. The code provided attempts to implement these methods but raises concerns about the correctness and adherence to Java's contract for these methods. Key issues identified include the potential for hashCode() to change during the object's lifetime, which could cause problems when using collections like HashSet. Additionally, the relationship between equals() and hashCode() is clarified, emphasizing that while equal objects must have the same hash code, the reverse is not guaranteed. The user seeks to load song data from a text file into a HashSet and format it correctly, highlighting the importance of understanding the Java documentation for these methods.
STEMucator
Homework Helper
Messages
2,076
Reaction score
140

Homework Statement



I'm trying to override the hashcode(), equals() and compareTo() methods in a class.

Homework Equations



Java.

The Attempt at a Solution



Here's the code :

Code:
package data_cleaner;

import java.util.*;

public class SongEntry implements Comparable<SongEntry> {

    private String studentNumber;
    private String bookCode;
    private int pageNumber;
    private String songTitle;

    public SongEntry(String studentNumber, String bookCode, int pageNumber, String songTitle) {
        this.studentNumber = studentNumber;
        this.bookCode = bookCode;
        this.pageNumber = pageNumber;
        this.songTitle = songTitle;
    }
    
    /*Getters and setters*/
    public String getSongTitle() { return songTitle; }
    public String getStudentNumber() { return studentNumber; }
    public String getBookCode() { return bookCode; }
    public int getPageNumber() { return pageNumber; }
    public void setSongTitle(String newSongTitle) { songTitle = newSongTitle; }

    @Override
    /*Returns a String representation of a SongEntry*/
    public String toString() {
        return (studentNumber + " " + bookCode + " " + pageNumber + " " + songTitle);
    }

    @Override
    /*Compares two songs to see if they are equal to each other by comparing song titles, book code and page numbers*/
    public boolean equals(Object obj) {
        if (!(obj instanceof SongEntry))
            return false;
        return(getBookCode().equals(((SongEntry)obj).getBookCode()) && getPageNumber() == ((SongEntry)obj).getPageNumber() && getSongTitle().equals( ((SongEntry)obj).getSongTitle()));
    }

    @Override
    /*Retrieve the name of the song and loop through the name adding on (i+1)*ASCIIVALUE of each char
    to the hashvalue which will be returned*/

    public int hashCode() {
    	int hashValue = 0;
    	for(int i=0;i<getSongTitle().length();i++)
    		hashValue += ((int)getSongTitle().charAt(i))*(Integer.parseInt(getStudentNumber()))*(getPageNumber());
    	return hashValue;
    }
    
    @Override
    /*Recall x.equals(y) iff x.hashcode() == y.hashcode(). Also, x.equals(y) iff x.compareTo(y) == 0.
    Hence x.compareTo(y) == 0 iff x.hashcode() - y.hashcode() == 0.
This implies we can subtract the hashcode of y from x to compare each songEntry and return it.*/
    public int compareTo(SongEntry aSongEntry) {
    	return (hashCode() - aSongEntry.hashCode());
    }
}

I don't believe the methods I wrote are correct? I believe there are rules which I've missed, but I cannot see where I've made a mistake?
 
Last edited by a moderator:
Physics news on Phys.org


I didn't look in too much detail, but I propose you look for the documentation of .equals() and .hashCode() again. They can be a bit intimidating, but if you are writing such code it will be useful to read it thoroughly and understand it. And you can use it as a sort of checklist to make sure you satisfy the requirements.

Two of the flaws that I noticed first were that hashCode() can change during the lifetime of the object, and I think if you want to stick it in hashmaps that's not such a good idea. Since the student number, book number and page number have no setters, I would use one of those instead (or a combination of them, of course).

The second thing is that you wrote " x.equals(y) iff x.hashcode() == y.hashcode()". The "only if" is true (two equal objects have the same hash code) but the "if" is not. Two non-equal objects can perfectly well share a hash code, although for efficiency in storing data in hashmaps it is recommended that they are spread out as uniformly through the allowed range of hash codes as possible (though this is the least critical requirement, I think, because although it can degrade your maps performance to linear search of a single list, at least it it won't break them; since it is often hard to satisfy all requirements for a general type this is the most disregarded item.)
 


CompuChip said:
I didn't look in too much detail, but I propose you look for the documentation of .equals() and .hashCode() again. They can be a bit intimidating, but if you are writing such code it will be useful to read it thoroughly and understand it. And you can use it as a sort of checklist to make sure you satisfy the requirements.

Two of the flaws that I noticed first were that hashCode() can change during the lifetime of the object, and I think if you want to stick it in hashmaps that's not such a good idea. Since the student number, book number and page number have no setters, I would use one of those instead (or a combination of them, of course).

The second thing is that you wrote " x.equals(y) iff x.hashcode() == y.hashcode()". The "only if" is true (two equal objects have the same hash code) but the "if" is not. Two non-equal objects can perfectly well share a hash code, although for efficiency in storing data in hashmaps it is recommended that they are spread out as uniformly through the allowed range of hash codes as possible (though this is the least critical requirement, I think, because although it can degrade your maps performance to linear search of a single list, at least it it won't break them; since it is often hard to satisfy all requirements for a general type this is the most disregarded item.)

I'm not using hashmaps, I'm using hashset.

The JAVA Api and examples are not clear at all about the Collection interface. I've read them 8 times at least.

I'll be more clear about my objective, perhaps it would make things easier to communicate.

I want to be able to load data from a text file ( which I've done using a BufferedReader ) which is in the format :

studentnumber,bookcode,page#,songname

Example : 10101010,AAAA,999,"Hello"
Example : 10000001,BBBB,000,That's, cool

line by line. Each line is one after the other in the text file so I can use readLine() to read it into a String or StringBuilder.

Then I want to be able to format the data ( The songname portion in particular since everything else doesn't matter ) and create a SongEntry object using the constructor.

I'll then store those SongEntry objects into my hashset and use PrintWriter to output this newly formatted data to a new text file.

Here's my code from my other class if it helps ( main at the bottom ) :

Code:
package data_cleaner;

import java.util.*;
import java.io.*;

public class DataCleaner {

    //student number to identify student
    private String studentNumber = "100804018";
    //set to hold song entries
    private HashSet<SongEntry> songDataSet = new HashSet<SongEntry>();
    
    //default constructor
    public DataCleaner() {
    }

    //Getters and setters
    public HashSet<SongEntry> getSongDataSet() { return songDataSet; }
    public String getStudentNumber() { return studentNumber; }
    
    //Load data, clean it and then put it into the hashset
    public void loadData(String dataFile) {
    	int numberOfSongsRead = 0;

    	try {
    		//Load the text file
	    	BufferedReader file = new BufferedReader(new FileReader(dataFile));
	    	
	    	//Read each line until EOF and clean it up
	    	while(file.ready()){
	    		StringBuilder songData = new StringBuilder((file.readLine()).trim());
	    		numberOfSongsRead++;
	    		boolean isThe = false;
	    		int counter = 0;
	    	
	    		//Eliminates unnecessary commas 
	    		for(int i=0;i<songData.length();i++) {
	    			if(songData.charAt(i) == ',' && counter < 3)
	    				counter++;
	    			else if(songData.charAt(i) == ',')
	    				songData.setCharAt(i, ' ');
	    		}

	    		//Split the newly formatted data for ease of manipulation and reset the string builder for usage
	    		String[] splitSongData = (songData.toString()).split(",");
    			splitSongData[3] = splitSongData[3].toLowerCase();
	    		songData = new StringBuilder();
	    		songData.append('"');
	    		
	    		//Loop through each song name. Clean it and put it inside of the hashset
	    		for(int i=0;i<splitSongData[3].length();i++) {

	    			//Check for the word 'the' in any form at the start of a song name.
	    			if(i<2)
	    				if(splitSongData[3].charAt(i) == 'T' || splitSongData[3].charAt(i) == 't')
	    					if( (i+1 < splitSongData[3].length()) && (splitSongData[3].charAt(i+1) == 'H' || splitSongData[3].charAt(i+1) == 'h') )
	    						if( ( i+2 < splitSongData[3].length() ) && ( splitSongData[3].charAt(i+2) == 'E' || splitSongData[3].charAt(i+2) == 'e' ) )
	    							isThe = true;
	    		
		    		//These next two ifs will handle capitalization at the start of a song name	
		    		if(i==0 && Character.isLowerCase(splitSongData[3].charAt(i))) {
		    			songData.append(Character.toUpperCase(splitSongData[3].charAt(i)));
		    			continue;
		    		}
		    			
		    		if(i<2 && Character.isLowerCase(splitSongData[3].charAt(i)))
		    			if(splitSongData[3].charAt(i-1) == '"' || splitSongData[3].charAt(i-1) == '(') {
		    				songData.append(Character.toUpperCase(splitSongData[3].charAt(i)));
		    				continue;
		    			}
		    		
		    		//These next two ifs will handle capitalization mid-name as well as default appending behaviour
		    		if(Character.isLowerCase(splitSongData[3].charAt(i)) && Character.isWhitespace(splitSongData[3].charAt(i-1))) {
		    			songData.append(Character.toUpperCase(splitSongData[3].charAt(i)));
		    			continue;
		    		}
		    			
		    		if(splitSongData[3].charAt(i) != '"')
		    			songData.append(splitSongData[3].charAt(i));
		    	}
		    		
	    		//Some minor touch ups as per formatting
		    	if(isThe)
		    		songData.append(", The\"");
		   		else
		   			songData.append('"');
	    		
		    	getSongDataSet().add(new SongEntry(splitSongData[0], splitSongData[1], (int)(Integer.parseInt(splitSongData[2])), songData.toString()));
	    	}	
    	}
    	
    	catch (FileNotFoundException e) {  
    		System.out.println("Error: Cannot find file");  
    	} 
    	catch (IOException e) {  
    		System.out.println("Error: Cannot read from file");  
    	}
    	
    	System.out.println(getSongDataSet().size());
    	//for(SongEntry s : getSongDataSet())
    		//System.out.println(s);
    	
    	System.out.println("A data file has been read! Ammount of songs read from data file : " + numberOfSongsRead);

    }

    public void cleanData() {
    	TreeSet<SongEntry> alphaTree = new TreeSet<SongEntry>();
    	alphaTree.addAll(getSongDataSet());
    	//for(SongEntry s : alphaTree)
    		//System.out.println(s);
    }

    public void exportData(String toFileName) {
    	try {
	    	PrintWriter	aFile = new PrintWriter(new FileWriter("outputdata.txt"));
	    	for(SongEntry s : getSongDataSet())
	    		aFile.println(s.toString());
	    	aFile.close();
    	}
    	catch (FileNotFoundException e) {  
    		System.out.println("Error: Cannot find file");  
    	} 
    	catch (IOException e) {  
    		System.out.println("Error: Cannot read from file");  
    	}
    }
    
    public void exportMyData(String toFileName) {
    	try {
	    	PrintWriter	aFile = new PrintWriter(new FileWriter("myoutputdata.txt"));
	    	for(SongEntry s : getSongDataSet()) {
	    		if(s.getStudentNumber().equals(studentNumber))
	    			aFile.println(s.toString());
	    	}
	    	aFile.close();
    	}
    	catch (FileNotFoundException e) {  
    		System.out.println("Error: Cannot find file");  
    	} 
    	catch (IOException e) {  
    		System.out.println("Error: Cannot read from file");  
    	}    
    }
    
    public void resetData() {
        //reset all gathered data
        songDataSet.clear();
    }

    public void runDataCleaner() {
        //Perform the steps of the data cleaner exercise
        System.out.println("Running Data Cleaner");

        //Step 1: Read master data into a set
        loadData("fakebookdata.txt");
        
        //Step 2: Read Student contribution file into set
        loadData("mydata.txt");
        
        //Step 3: Do additional cleaning not done during loading
        cleanData();
        
        //Step 4: Export sorted and cleaned data
        exportData("outputdata.txt");
        
        //Step 5: Export only the data I contributed
        exportMyData("myoutputdata.txt");
    }

    public static void main(String[] args) {
        System.out.println("Starting Fake Book Data Cleaner");
        DataCleaner app = new DataCleaner();
        app.runDataCleaner();
    }
}

Also I believe I'm supposed to be able to use the hashcode(), equals() and compareTo() to my advantage to reduce the amount of code I needed?
 
Thread 'Have I solved this structural engineering equation correctly?'
Hi all, I have a structural engineering book from 1979. I am trying to follow it as best as I can. I have come to a formula that calculates the rotations in radians at the rigid joint that requires an iterative procedure. This equation comes in the form of: $$ x_i = \frac {Q_ih_i + Q_{i+1}h_{i+1}}{4K} + \frac {C}{K}x_{i-1} + \frac {C}{K}x_{i+1} $$ Where: ## Q ## is the horizontal storey shear ## h ## is the storey height ## K = (6G_i + C_i + C_{i+1}) ## ## G = \frac {I_g}{h} ## ## C...

Similar threads

Replies
2
Views
1K
Replies
1
Views
2K
Replies
1
Views
3K
Replies
3
Views
4K
Replies
9
Views
3K
Replies
1
Views
2K
Replies
1
Views
2K
Replies
1
Views
3K
Back
Top