I'm testing my code using JUnit4 to see if an insertion sort of an array of objects (Word(String a, int b)) is correctly sorting the array. The problem I'm having is that when I run JUnit it fails, giving me an error: "expected {One, 1} but was {One, 1}." If I print out both values I'm comparing before I run the test they are also the same. The code is:
package sort;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class InsertionTest {
private Word word1;
private Word word2;
private Word word3;
private Word word4;
private Word wordExpected1;
private Word wordExpected2;
private Word wordExpected3;
private Word wordExpected4;
#Before
public void setUp() throws Exception {
word1 = new Word("One", 1);
word2 = new Word("Two", 2);
word3 = new Word("Three", 3);
word4 = new Word("Four", 4);
wordExpected1 = new Word("One", 1);
wordExpected2 = new Word("Two", 2);
wordExpected3 = new Word("Three", 3);
wordExpected4 = new Word("Four", 4);
}
#After
public void tearDown() throws Exception {
}
#SuppressWarnings("deprecation")
#Test
public void test() {
Word[] wordList = { word3, word2, word4, word1 };
Word[] expected = { wordExpected1, wordExpected2, wordExpected3, wordExpected4 };
Insertion.sortInsert(wordList);
assertEquals(expected, wordList);
}
}
The code for the insertionsort:
package sort;
public class Insertion {
/**
* regular insertion sort
* #param x - the input array containing scores of words that need to be sorted.
*/
public static void sortInsert ( Word[] x) {
int N = x.length;
for (int i = 1; i < N; i++){
int tempScore = x[i].getScore();
String tempWord = x[i].getWord();
int j;
for (j = (i - 1); j >= 0 && tempScore < x[j].getScore(); j--){
x[j + 1].setScore(x[j].getScore());
x[j + 1].setWord(x[j].getWord());
}
x[j + 1].setScore(tempScore);
x[j + 1].setWord(tempWord);
}
}
}
The code for the ADT:
package sort;
public class Word implements Comparable<Word>{
private String word;
private int score;
public Word(String w, int s){
this.word = w;
this.score = s;
}
public int getScore(){
return score;
}
public void setScore(int s){
score = s;
}
public String getWord(){
return word;
}
public void setWord(String w){
word = w;
}
#Override
public int compareTo(Word w){
if ((this.score) > (w.score)) { return 1; }
else if ((this.score) < (w.score)) { return -1; }
return 0;
}
public String toString(){
return ("{" + this.word + "," + this.score + "}");
}
}
Any help would be appreciated, thank you!
You're creating two different objects. Just because their attributes have the same value, they are not necessarily equal. To achive this, you need to override the equals() method in class Word.
Thus, add:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Word other = (Word) obj;
if (score != other.score)
return false;
if (word == null) {
if (other.word != null)
return false;
} else if (!word.equals(other.word))
return false;
return true;
}
Eclipse provides an easy way to do this (semi-)automatically. Open your Word class, select Source -> Generate hashCode() and equals()...
Select attributes that should be considered when checking two Word objects for equality.
Also, you should oderride hashCode().
Related questions:
Why should I override hashCode() when I override equals() method?
What issues should be considered when overriding equals and hashCode in Java?
By the way:
Might be a copy&paste issue, but implemented methods from interfaces are not being annotated with #Override (as your compareTo() is). #Override annotation would be appropriate for toString() since you override the toSting()-method of class Object.
From #Override Javadoc:
Indicates that a method declaration is intended to override a method declaration in a supertype.
JUnit's assertEquals depends on you correctly implementing Object.equals(Object), which you didn't do. Implement equals(Object) in Word to make this work.
Use Lombok to generate the equal and hashcode method. Then it will work. Your code will also become clean by using Lombok annotations.
Related
We all know how to correctly check for fractional numbers in tests (using TOLERANCE):
class OxygenTankTest {
static final double TOLERANCE = 0.001;
#Test
void testFilling() {
OxygenTank tank = OxygenTank.withCapacity(100);
tank.fill(5.8);
tank.fill(5.6);
Assertions.assertEquals(0.114, tank.getStatus(), TOLERANCE);
}
}
But my question is how to check if we need to check not the individual values - but whole objects.
For example:
Need to test Summer - which performs the summation of fields
public class Summer {
public void setSum(Item itemTo, Item itemFrom) {
itemTo.setDiameter(itemTo.getDiameter() + itemFrom.getDiameter());
itemTo.setLength(itemTo.getLength() + itemFrom.getLength());
}
}
public class Item {
private Double diameter;
private Double length;
public Item(Double diameter, Double length) {
this.diameter = diameter;
this.length = length;
}
public Double getDiameter() {
return diameter;
}
public void setDiameter(Double diameter) {
this.diameter = diameter;
}
public Double getLength() {
return length;
}
public void setLength(Double length) {
this.length = length;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
if (diameter != null ? !diameter.equals(item.diameter) : item.diameter != null) return false;
return length != null ? length.equals(item.length) : item.length == null;
}
#Override
public int hashCode() {
int result = diameter != null ? diameter.hashCode() : 0;
result = 31 * result + (length != null ? length.hashCode() : 0);
return result;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("Item{");
sb.append("diameter=").append(diameter);
sb.append(", length=").append(length);
sb.append('}');
return sb.toString();
}
}
How i try to write a test:
public class SummerTest {
#Test
public void setSum() {
Summer summer = new Summer();
Item itemFrom = new Item(2.321, 1.111);
Item itemTo = new Item(0.999, 0.999);
summer.setSum(itemFrom, itemTo);
// expected
Item expectedItem = new Item(3.32, 2.11);
assertThat(itemFrom, equalTo(expectedItem));
}
}
But it does not work!
java.lang.AssertionError:
Expected: <Item{diameter=3.32, length=2.11}>
but: was <Item{diameter=3.3200000000000003, length=2.11}>
Expected :<Item{diameter=3.32, length=2.11}>
Actual :<Item{diameter=3.3200000000000003, length=2.11}>
<Click to see difference>
How to properly check for compliance?
You overwrote the equals method that checks for exact equality. If you have objects that contain floating point values (float, double) that are considered in your equals implementation, you will want to not compare the object itself, but the values within the object:
assertEquals(expected.getDiameter(), itemFrom.getDiameter(), TOLERANCE);
assertEquals(expected.getLength(), itemFrom.getLength(), TOLERANCE);
Or if you want to get fancy you can create your own Matcher that goes into the assertThat.
Consider changing the set up of the test to allow for exact comparisons via Item::equals:
private static final double ITEM_FROM_DIAMETER = 2.321;
private static final double ITEM_FROM_LENGTH = 1.111;
private static final double ITEM_TO_DIAMETER = 0.999;
private static final double ITEM_TO_LENGTH = 0.999;
Item itemFrom = new Item(ITEM_FROM_DIAMETER, ITEM_FROM_LENGTH);
Item itemTo = new Item(ITEM_TO_DIAMETER, ITEM_TO_LENGTH);
Item expectedItem = new Item(ITEM_FROM_DIAMETER + ITEM_TO_DIAMETER, ITEM_FROM_LENGTH + ITEM_TO_LENGTH);
Also, since Item is mutable, it would be a good idea to assert itemFrom was not changed.
I have this homework assignment to make a recursive method to crack a password of a given length, n (unlimited and unknown!) made of small English letters, a-z ONLY.
Here's the class "Password" that creates a random password:
import java.util.Random;
public class Password {
private String _password = "";
public Password(int length) {
Random generator = new Random();
for (int i = 0; i < length; ++i) {
this._password = this._password + (char) (generator.nextInt(26) + 97);
}
}
public boolean isPassword(String st) {
return st.equals(this._password);
}
public String getPassword() {
return this._password;
}
}
And here is the question in detail:
"You must write a static recursive method,
public static String findPassword(Password p, int length) that "cracks" the code.
Here's an example of a main method:
public class Main {
public static void main(String[] args) {
Password p = new Password(5);
System.out.println(p.getPassword());
System.out.println(Ex14.findPassword(p, 5));
}
}
Important notes:
The method MUST be recursive, without using ANY loops.
You may not use the getPassword method.
If you would like to use a method of the String class, you may only use the following: charAt, substring, equals, length.
You MAY use overloading, but you MAY NOT use other methods. (You cannot use String.replace/String.replaceall)
You MAY NOT use static(global) variables.
You MAY NOT use any Array. "
Here's what I have until now, which clearly doesn't work; :\
public static String findPassword(Password p, int length) {
return findPassword(p, length, "", 'a');
}
public static String findPassword(Password p, int length, String testPass, char charToChange) {
int currDig = testPass.length() - 1;
if (p.isPassword(testPass))
return testPass;
if (length == 0) // There is no password.
return ""; // Returns null and not 0 because 0 is a password.
if (length > testPass.length())
return findPassword(p, length, testPass + charToChange, charToChange);
if (testPass.length() == length) {
//TODO if charToChange is 'z', then make it the one before it '++', and reset everything else to a.
//if (charToChange == 'z') {
// charToChange = 'a';
// String newString = testPass.substring(0, currDig-1) +
// (charToChange++)
// +testPass.substring(currDig+1,testPass.length()-1);
System.out.println("it's z");
// TODO currDig --;
// String newerString = testPass.substring(0, currDig - 1)
// + (char) (testPass.charAt(testPass.length() - 1) - 25);
// currDig--;
}
return "";
}
Thank you very much! much appreciated!
- TripleS
This is my code and it compiles fine but when I try to create a string it says
Error: cannot find symbol - variable racer
public class Word {
private String original;
public Word(String s) {
original = s;
}
public String reverse () {
String reverse= "";
int x = 1;
int length = original.length();
while (length - x >= 0) {
reverse = reverse + original.substring(length -x);
x++;
}
return reverse;
}
public boolean isPalindrome() {
if(original.equals(reverse()))
return true;
else
return false;
}
}
The stated problem is not in the code posted - my guess is irrelephant's comment is correct, ie change new Word(racer) --> new Word("racer").
But I offer this to eliminate any chance of any errors in your code by basically eliminating your code:
public class Word {
private String original;
public Word(String s) {
original = s;
}
public boolean isPalindrome()
return new StringBuilder(original).reverse().toString().equals(original);
}
}
or if you must expose a reverse() method:
public class Word {
private String original;
public Word(String s) {
original = s;
}
public String reverse () {
return new StringBuilder(original).reverse().toString();
}
public boolean isPalindrome()
return reverse().equals(original);
}
}
I don't see the variable racer anywhere, but since you're using reverse inside a method, I'd recommend making it
Most likely, racer was never defined
Either that or the method was called w/o quotes
isPalindrome(racer)//note the lack of quotes
change reverse() to this
private() String reverse () {
String reverse= "";
int x = 1;
int length = original.length();
while (length - x >= 0) {
reverse = reverse + original.substring(length -x);
x++;
}
return reverse;
I'm attempting to return the index of where an object appears in an array of objects.
public static int search(WordCount[] list,WordCount word, int n)
{
int result = -1;
int i=0;
while (result < 0 && i < n)
{
if (word.equals(list[i]))
{
result = i;
break;
}
i++;
}
return result;
}
WordCount[] is the array of objects.
word is an instance of WordCount.
n is the number of objects in WordCount[]
It runs, but isn't returning the index correctly. Any and all help is appreciated. Thanks for your time.
CLASS
class WordCount
{
String word;
int count;
static boolean compareByWord;
public WordCount(String aWord)
{
setWord(aWord);
count = 1;
}
private void setWord(String theWord)
{
word=theWord;
}
public void increment()
{
count=+1;
}
public static void sortByWord()
{
compareByWord = true;
}
public static void sortByCount()
{
compareByWord = false;
}
public String toString()
{
String result = String.format("%s (%d)",word, count);
return result;
}
}
How I'm calling it...
for (int i=0;i<tokens.length;i++)
{
if (tokens[i].length()>0)
{
WordCount word = new WordCount(tokens[i]);
int foundAt = search(wordList, word, n);
if (foundAt >= 0)
{
wordList[foundAt].increment();
}
else
{
wordList[n]=word;
n++;
}
}
}
}
By default, Object#equals just returns whether or not the two references refer to the same object (same as the == operator). Looking at what you are doing, what you need to do is create a method in your WordCount to return word, e.g.:
public String getWord() {
return word;
}
Then change your comparison in search from:
if (word.equals(list[i]))
to:
if (word.getWord().equals(list[i].getWord()))
Or change the signature of the method to accept a String so you don't create a new object if you don't have to.
I wouldn't recommend overriding equals in WordCount so that it uses only word to determine object equality because you have other fields. (For example, one would also expect that two counters were equal only if their counts were the same.)
The other way you can do this is to use a Map which is an associative container. An example is like this:
public static Map<String, WordCount> getCounts(String[] tokens) {
Map<String, WordCount> map = new TreeMap<String, WordCount>();
for(String t : tokens) {
WordCount count = map.get(t);
if(count == null) {
count = new WordCount(t);
map.put(t, count);
}
count.increment();
}
return map;
}
This method is probably not working because the implementation of .equals() you are using is not correctly checking if the two objects are equal.
You need to either override the equals() and hashCode() methods for your WordCount object, or have it return something you want to compare, i.e:word.getWord().equals(list[i].getWord())
It seems easier to use:
public static int search(WordCount[] list, WordCount word)
{
for(int i = 0; i < list.length; i++){
if(list[i] == word){
return i;
}
}
return -1;
}
This checks each value in the array and compares it against the word that you specified.
The odd thing in the current approach is that you have to create a new WordCount object in order to look for the count of a particular word. You could add a method like
public boolean hasEqualWord(WordCount other)
{
return word.equals(other.word);
}
in your WordCount class, and use it instead of the equals method:
....
while (result < 0 && i < n)
{
if (word.hasEqualWord(list[i])) // <--- Use it here!
{
....
}
}
But I'd recommend you to rethink what you are going to model there - and how. While it is not technically "wrong" to create a class that summarizes a word and its "count", there may be more elgant solutions. For example, when this is only about counting words, you could consider a map:
Map<String, Integer> counts = new LinkedHashMap<String, Integer>();
for (int i=0;i<tokens.length;i++)
{
if (tokens[i].length()>0)
{
Integer count = counts.get(tokens[i]);
if (count == null)
{
count = 0;
}
counts.put(tokens[i], count+1);
}
}
Afterwards, you can look up the number of occurrences of each word in this map:
String word = "SomeWord";
Integer count = counts.get(word);
System.out.println(word+" occurred "+count+" times);
Intro
My code to do a custom sort by using Comparable is not work the way I want it to. I'm basically taking an Array of directories and sorting them by:
First number of directories, the fewer comes first.
If it's a tie alphabetically.
The problem
An example of an input you be:
["/", "/usr/", "/usr/local/", "/usr/local/bin/", "/games/",
"/games/snake/", "/homework/", "/temp/downloads/" ]
Which should return this:
["/", "/games/", "/homework/", "/usr/", "/games/snake/",
"/temp/downloads/", "/usr/local/", "/usr/local/bin/" ]
But for some reason my code is return this:
["/", "/usr/", "/games/", "/homework/", "/usr/local/",
"/games/snake/", "/usr/local/bin/", "/temp/downloads/" ]
My code [edited with comments]
import java.util.*;
public class Dirsort { public String[] sort(String[] dirs) {
//Creates Array list containing Sort object
ArrayList<Sort> mySort = new ArrayList<Sort>();
//Loop that gets the 3 needed values for sorting
for (String d: dirs){
String [] l = d.split("/");//String array for alphabetical comparison
int di = d.length();//Length of array for sorting by number of directories
mySort.add(new Sort(di,l,d));//adds Sort object to arraylist (note d (the entire directory) is needed for the toString)
}
Collections.sort(mySort);//sorts according to compareTo
String [] ans = new String [mySort.size()];//Creates a new string array that will be returned
int count = 0;//to keep track of where we are in the loop for appending
for (Sort s: mySort){
ans[count] = s.toString();
count++;
}
return ans;
}
class Sort implements Comparable<Sort>{
private int d;//number of directories
private String [] arr;//array of strings of names of directories
private String dir;//full directory as string for toString
//Constructor
public Sort(int myD, String [] myArr, String myDir){
d = myD;
arr = myArr;
dir = myDir;
}
//toString
public String toString(){
return dir;
}
#Override
public int compareTo(Sort arg0) {
// TODO Auto-generated method stub
//If they are the same return 0
if (this.equals(arg0)){
return 0;
}
//if the directories are empty
if("/".equals(arg0.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
//If they are not the same length the shorter one comes first
if (this.d != arg0.d){
return this.d - arg0.d;
}
//If they are the same length, compare them alphabetically
else{
for (int i = 0; i < arg0.d; i++){
if (!this.arr[i].equals(arg0.arr[i])){
return this.arr[i].compareTo(arg0.arr[i]);
}
}
}
return 0;
}
}
}
The bug is here:
for (String d: dirs){
String [] l = d.split("/");
int di = d.length(); // <- here
mySort.add(new Sort(di,l,d));
}
Because there you are comparing the length of the entire directory String, not the number of 'folders' in the directory. That's why "/usr/" comes before "/homework/", for example, because:
"/usr/".length() == 5
"/homework/".length() == 10
I believe what you wanted was this, using the length of the split:
int di = l.length;
Then the output is:
/
/games/
/homework/
/usr/
/games/snake/
/temp/downloads/
/usr/local/
/usr/local/bin/
There's another small bug though (possibly), which is that calling split on a String that starts with the delimiter will result in an empty String at the beginning.
IE:
"/usr/".split("/") == { "", "usr" }
So you might want to do something about that. Though here it means that all of them start with the empty String so it doesn't end up with an effect on the way you're doing the comparison.
And as a side note, it's also true what #JBNizet is suggesting that giving your variables more meaningful names helps a lot here. fullDir.length() and splitDir.length would have made this much easier to spot (and it may have never happened in the first place).
Here's a fixed version of your code, which handles the case where both directories are "/", which removes the unnecessary, and incorrectly passed length of the parts array, and which uses more meaningful variable names:
public class Dirsort {
public static void main(String[] args) {
String[] input = new String[] {
"/",
"/usr/",
"/usr/local/",
"/usr/local/bin/",
"/games/",
"/games/snake/",
"/homework/",
"/temp/downloads/"
};
String[] result = new Dirsort().sort(input);
System.out.println("result = " + Arrays.toString(result));
}
public String[] sort(String[] dirs) {
ArrayList<Sort> sorts = new ArrayList<Sort>();
for (String dir : dirs) {
String[] parts = dir.split("/");
sorts.add(new Sort(parts, dir));
}
Collections.sort(sorts);
String[] result = new String[sorts.size()];
int count = 0;
for (Sort sort: sorts) {
result[count] = sort.toString();
count++;
}
return result;
}
class Sort implements Comparable<Sort> {
private String[] parts;
private String dir;
public Sort(String[] parts, String dir) {
this.parts = parts;
this.dir = dir;
}
public String toString(){
return dir;
}
#Override
public int compareTo(Sort other) {
if (this.equals(other)){
return 0;
}
if("/".equals(other.dir) && "/".equals(dir)) {
return 0;
}
if("/".equals(other.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
if (this.parts.length != other.parts.length){
return this.parts.length - other.parts.length;
}
else {
for (int i = 0; i < other.parts.length; i++){
if (!this.parts[i].equals(other.parts[i])){
return this.parts[i].compareTo(other.parts[i]);
}
}
}
return 0;
}
}
}
I spotted the problem by simply using my debugger and make it display the value of all the variables.
public class Disort
{
public static String[] sort(String[] dirs)
{
ArrayList<Path> mySort = new ArrayList<Path>();
Path pathDir;
for(String dir : dirs){
pathDir = Paths.get(dir);
// check if directory exists
if(Files.isDirectory(pathDir)){
mySort.add(pathDir);
}
}
// sort the ArrayList according a personalized comparator
Collections.sort(mySort, new Comparator<Path>(){
#Override
public int compare(Path o1, Path o2)
{
if(o1.getNameCount() < o2.getNameCount()){
return -1;
}
else if(o1.getNameCount() > o2.getNameCount()){
return 1;
}
else{
return o1.compareTo(o2);
}
}
});
// to return a String[] but it will better to return a ArrayList<Path>
String[] result = new String[mySort.size()];
for(int i = 0; i < result.length; i++){
result[i] = mySort.get(i).toString();
}
return result;
}
}