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.
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?
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 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?
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...