I am getting an unexpected output for this. Please have a look. I am not able to find the problem. What's wrong with my program? Can anybody explain? I am getting the output
Joe Sue Mike Clare Juliet
Joe Mike Clare Juliet
objects in TreeSets and TreeMaps and with Collections.sort() for Lists, using the Comparable Interface.
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
class Person implements Comparable<Person> {
private String name;
public Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
#Override
public int compareTo(Person person) {
int len1 = name.length();
int len2 = person.name.length();
if(len1 > len2) {
return 1;
}
else if(len1 < len2) {
return -1;
}
else {
return 0;
}
}
}
public class App {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
SortedSet<Person> set = new TreeSet<Person>();
//adding Element
addElements(list);
addElements(set);
//sorting element
Collections.sort(list);
//displaying result
showElements(list);
System.out.println();
showElements(set);
}
//adding element methods
private static void addElements(Collection<Person> col) {
col.add(new Person("Joe"));
col.add(new Person("Sue"));
col.add(new Person("Juliet"));
col.add(new Person("Clare"));
col.add(new Person("Mike"));
}
private static void showElements(Collection<Person> col) {
for(Person element: col) {
System.out.println(element);
}
}
}
You are comparing the persons by the length of their names. And the names "Joe" and "Sue" have the same length. So only one of them can occur in the TreeSet. However, this comparison criterion is not consistent with the implementation of equals!
You should place your Person objects into a list, and sort this list with Collections#sort - preferably, with an own Comparator. Also see https://stackoverflow.com/a/21659849
EDIT: Further explaination:
A Set can contain each element only once. And by the way that you specified your compareTo method, you impled that "Sue" and "Joe" are equal (because their names have equal lengths). So they can not both appear in a Set.
Note: They are not really equal, based on the equals method. But the TreeSet uses the compareTo method, and this compareTo method is currently not consistent with equals. So the Set shows a wrong behavior because of your wrong compareTo method.
EDIT: A possible solution:
If the names have equal lengths, you can compare them alphabetically. This way, the compareTo method becomes consistent with equals: It will return 0 if and only if the names are equal.
#Override
public int compareTo(Person person) {
int len1 = name.length();
int len2 = person.name.length();
if(len1 > len2) {
return 1;
}
else if(len1 < len2) {
return -1;
}
else {
return name.compareTo(person.name);
}
}
A SortedSet uses compareTo for evaluation if an element can be added to the Set. Your input contains two names with same length (your comparison criterion), hence one of Joe or Sue must be filtered out (in your case Sue).
The TreeSet documentation talks about having(not mandatory) compareTo implementaion consistent with equals. In your case 'Joe' and 'Sue' are of equal length and as per your compareTo implementation they are same. One workaround would be to compare the hashcode values in compareTo methods if they are of equal length.
Related
According to this answer, roughly, if we had a Classroom object array of student objects, class[index] != student1. I believe this is the mistake I am making in implementing my equals method to compare the array[index] object to another object. I believed the array[index] and the object I am comparing against to be the same.
The code below shows my getNumStudents method in which I try to count the number of times a student id shows up in a class. ID represents brand shoes he or she likes (practice exercise out of lecture). This method is in my classroom object class which implements an interface.
#Override
public int getNumStudents(T anEntry) {
int count = 0;
for (int index = 0; index < numberOfEntries; index++) {
if (roster[index].equals(anEntry)) )
{
counter++;
}
}
return count;
}
My equals method is as such and is implemented in the student class:
public boolean equals(Student student) {
if (this == student)
{
return true;
}
if (student == null)
{
return false;
}
if (this.getID() != student.getID())
{
return false;
}
return true;
}
I don't know if I properly did the hashCode override but here it is (in Student class):
#Override
public int hashCode() {
int result = 17;
result = 31 * result + studentID;
return result;
}
I've narrowed down where the bug is to most likely here:
if (roster[index].equals(anEntry)) )
specifically
roster[index].equals(anEntry))
What should I call or how should I adjust my getNumStudents(T anEntry) method to properly return the number of students with a certain ID (representing a shoe type) within a Classroom object array?
Your equals signature is wrong.
The correct signature of equals method must be as follows.
public boolean equals(Object other)
Then inside the method you should check if it is of comparable type and if you really need it to be of type Student, you have to check for this and return false otherwise.
In your case that would be a minimal change required for your implementation:
public boolean equals(Object other)
{
if (this == other)
{
return true;
}
// This also works if `other` is `null`
if (!(other instanceof Student))
{
return false;
}
// Now we cast it to `Student`
final Student student = (Student) other;
if (this.getID() != student.getID())
{
return false;
}
return true;
}
I need to extend AbstractTableModel to represent some data in a table. I have a class Car which should represent one item (row) in a table:
public class Car implements Comparable<Car> {
public String make;
public int year;
public double engineVol;
public double maxSpeed;
// ...getters/setters for the fields...
public Car (String make, int year, double engineVol, double maxSpeed) {
this.make = make;
this.year = year;
this.engineVol = engineVol;
this.maxSpeed = maxSpeed;
}
#Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Car car = (Car) other;
if (year != car.year) return false;
if (Double.compare(car.engineVol, engineVol) != 0) return false;
if (Double.compare(car.maxSpeed, maxSpeed) != 0) return false;
return make.equals(car.make);
}
#Override
public int hashCode() {
int result;
long temp;
result = make.hashCode();
result = 31 * result + year;
temp = Double.doubleToLongBits(engineVol);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxSpeed);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
#Override
public int compareTo(Car other) {
return this.make.compareTo(other.make);
}
}
These Car objects are stored in a HashSet, which resides in the CarTableModel:
public class CarTableModel extends AbstractTableModel {
private static final long serialVersionUID = 7927259757559420606L;
private HashSet<Car> cars;
public CarTableModel(HashSet<Car> cars) {
this.cars = cars;
}
#Override
public int getRowCount() {
return cars.size();
}
#Override
public int getColumnCount() {
return 4;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return null;
}
}
As far as I get it I need to override at least 3 methods in an AbstractTableModel. How do I override getValueAt for a HashSet? What are those rowIndex and columnIndex arguments concerning the HashSet? How are those indeces are applied to the HashSet if we cannot get values from one by an index? Is it possible at all?
P.S. It's not my caprice to use a HashSet here, it's a university assignment, so it has to go this way.
How do I override getValueAt for a HashSet?
Don't use a HashSet. You would typically use an ArrayList so the rows are defined in the order you add them to the List.
For example check out Table Row Model which walks through the process of creating a custom model. It also provide a generic TableModel to make the process simpler if you wish.
It's not my caprice to use a HashSet here, it's a university assignment, so it has to go this way.
Missed that comment. I have no idea why you would be required use a HashSet since there is no direct way to access the data. So it seems to me like you need to iterate through the Set every time a row is required. Not very efficient.
You can try to use LinkedHashSet. But it is unclear how to use columnIndex with Car.
Maybe, ArrayTable from guava will help you.
You'll have to know that even if you think of using a Iterator to get to the nth value of your HashSet, the order is undefined.
Here, from the JavaDoc
It makes no guarantees as to the iteration order of the set; in
particular, it does not guarantee that the order will remain constant
over time.
Though, if you really have to use this collection, here is an implementation of getValueAt
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Optional<Car> tmpCar = cars.stream()
.skip(rowIndex) // Assuming your rows begin with 0
.findFirst();
return tmpCar.isPresent() ? tmpCar : null;
}
So, if is mandatory for you to use HashSet, you can do something like that:
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Car car = null;
int = 0;
for (Car c : cars) {
if (i == rowIndex) {
car = c;
break;
}
i++;
}
if (car == null) // handle this situation with your rules
return "";
return car.get<Field as described in your columns>; //
}
Bear in mind that is not guaranteed the order. Then test it a lot.
I am trying to read from a file and count how many times each string appears in the file. I am using a HashSet on the Object Item which i have created as follows :
Now in my main i am trying to read the file and add each String in the file to my set. Also while adding i am trying to increment the count of an item in the set which is appearing more than once. Here's my implementation for that :
package pack;
public class Item {
public String name;
public int count=1;
public Item(String name)
{
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + count;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Item other = (Item) obj;
if (count != other.count)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
For an input file like this :
chair table teapot
teapot book table
chair floor ceiling
wall chair floor
it is giving an output as follows :
wall appears 1 times
book appears 1 times
table appears 2 times
floor appears 2 times
teapot appears 2 times
chair appears 1 times
ceiling appears 1 times
chair appears 2 times
Here the set is having duplicate elements which i don't want. What is the correct way to update the objects inside a set?
i think this'll help you.
Create list of all keywords, and use code below.
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("a");
// get all Unique keywords
Set<String> set = new HashSet<String>(list);
for(String keyword: set){
System.out.println(keyword + ": " + Collections.frequency(list, keyword));
}
}
output
b: appears 1 time
a: appears 2 time
Your Item class uses the count field in its definition of equals and hashCode. This means that when you call set.contains(i) for the second occurrence of the string, contains will return true since count==1. You then increment count, and when you call set.contains(i) for the third occurrence of the string contains will return false, since the count of the Item in the set does not match the count of the Item you are passing to contains.
To fix this, you should change your definition of equals and hashCode to consider only the string and not the count.
This implementation will work, but is overly complex. You could simply create a Map<String, Integer> and increase the Integer (count) each time you see a new occurrence of the string.
Your Implementation is right. But your Item class equals method has only problem.
In equals method you have used count variable also. But name is only the unique field in that class. You have used count+name as unique. So it will create problem.
HashSet uses hashCode and equals to determine identity, so you should change hashCode and equals to work with the name only when you don't want to include the count of items in the test for equality:
package pack;
public class Item {
public String name;
public int count=1;
public Item(String name)
{
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Item other = (Item) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
I think the problem is in your equals method, when you do this check:
if (count != other.count)
return false;
Have you considered using a HashMap for your problem: put the name in the key and the counter in the value. This way you don't need an Item class at all.
I have a list of objects I'm referring to as Artifacts. I need to sort alphabetically by the "Name" attribute and in numerical order by an attribute that Artifact has called "Level".
The Level is not always set in Artifact and in that case the entire collection should be alphabetical. If the Artifact has a Level then that takes precedence and should be sorted by order of Level.
The Artifacts need to be unique based upon the Name attribute. I could use a Set collection and override the equals method of the Artifact to sort Alphabetically. However, when I want to sort by Level, the equals method relevant to Name will throw off the results of this sort.
What collections and object structure should I use to remain unique by Name but also be able to sort by Level?
You'll want to look at the comparable interface and the comparator interface. Implement Comparable if this is the only way your objects can be compared, comparator otherwise.
I think java.util.TreeSet is good Container for your problem. It is Set and it uses Compareble mechanism.
So you have two options:
1) put Comparator into TreeSet constructor
2) make your Artifact implement Comparable
TIP: In compareTo method you can use compareTo method from String.
The code below will sort the set giving the precedence to the level and later the name. If a level is null, it will be placed at the beginning, treating it as it was a level 0. For null names, the Artifact will be positioned as it had an empty level. Hope that helps.
import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;
public class Artifact implements Comparable<Artifact> {
private String name;
private Integer level;
public Artifact(String name, Integer level) {
this.name = name;
this.level = level;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((level == null) ? 0 : level.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Artifact other = (Artifact) obj;
if (level == null) {
if (other.level != null)
return false;
} else if (!level.equals(other.level))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
#Override
public int compareTo(Artifact o) {
if (level == null){
return new Artifact(name, 0).compareTo(o);
}
if (name == null){
return new Artifact("", level).compareTo(o);
}
if (level.equals(o.level)) {
return name.compareTo(o.name);
} else {
return level.compareTo(o.level);
}
}
public String toString() {
return level + " " + name;
}
public static void main(String[] args) {
Artifact a1 = new Artifact("a", 1);
Artifact a2 = new Artifact("a", 2);
Artifact a3 = new Artifact("a", 3);
Artifact b1 = new Artifact("b", 1);
Artifact b2 = new Artifact("b", 2);
Artifact b2a = new Artifact("b", 2);
Artifact nullLevel = new Artifact("a",null);
Artifact nullName = new Artifact(null,2);
SortedSet<Artifact> set = new TreeSet<Artifact>();
set.add(a1);
set.add(a2);
set.add(a3);
set.add(b1);
set.add(b2);
set.add(b2a);
set.add(nullLevel);
set.add(nullName);
System.out.println(Arrays.toString(set.toArray()));
}
}
I need to sort the list in java as below:
List contains collection of objects like this,
List list1 = {obj1, obj2,obj3,.....};
I need the final list which has "lowest value" and "repetition of name should avoid".
Ex:
List list1 = {[Nellai,10],[Gujarath,10],[Delhi,30],[Nellai,5],[Gujarath,15],[Delhi,20]}
After Sorting , I need the list like this :
List list1 = {[Nellai,5],[Gujarath,10],[Delhi,20]};
I have 2 Delhi (30,20) in my list. But I need only one Delhi which has lowest fare (20).
How to do that it in java?
Gnaniyar Zubair
If order doesn't matter, a solution is to use a Map[String, Integer], add an entry each time you find a new town, update the value each time the stored value is less than the stored one and then zip all the pairs into a list.
Almost the same as #Visage answer, but the order is different:
public class NameFare {
private String name;
private int fare;
public String getName() {
return name;
}
public int getFare() {
return fare;
}
#Override public void equals(Object o) {
if (o == this) {
return true;
} else if (o != null) {
if (getName() != null) {
return getName().equals(o.getName());
} else {
return o.getName() == null;
}
}
return false;
}
}
....
public Collection<NameFare> sortAndMerge(Collection<NameFare> toSort) {
ArrayList<NameFare> sorted = new ArrayList<NameFare>(toSort.size());
for (NameFare nf : toSort) {
int idx = sorted.getIndexOf(nf);
if (idx != -1) {
NameFare old = sorted.get(idx);
if (nf.getFare() < old.getFare()) {
sorted.remove(idx);
sorted.add(nf);
}
}
}
Collections.sort(sorted, new Comparator<NameFare>() {
public int compare(NameFare o1, NameFare o2) {
if (o1 == o2) {
return 0;
} else {
if (o1.getName() != null) {
return o1.getName().compareTo(o2.getName());
} else if (o2.getName() != null) {
return o2.getName().compareTo(o1.getName());
} else {
return 0;
}
}
}
});
}
I would do it in two stages.
Firstrly sort the list using a custom comparator.
Secondly, traverse the list and, for duplicate entries (which will now be adjacent to each other, provided you worte your comparator correctly), remove the entries with the higher values.
If you want to avoid duplicates, perhaps a class like TreeSet would be a better choice than List.
I would use an ArrayList like this:
ArrayList<Name> listOne = new ArrayList<Name>();
listOne.add(new Name("Nellai", 10);
listOne.add(new Name("Gujarath", 10);
listOne.add(new Name("Delhi", 30);
listOne.add(new Name("Nellai", 5);
listOne.add(new Name("Delhi", 20);
Collection.sort(listOne);
Then create the Name class
class name implements Comparable
{
private String name;
private int number;
public Name(String name, int number)
{
this.name= name;
this.number= number;
}
public String getName()
{
return this.name;
}
public int getNumber()
{
return this.number;
}
public int compareTo(Object otherName) // must be defined if we are implementing //Comparable interface
{
if(otherName instanceif Name)
{
throw new ClassCastException("Not valid Name object"):
}
Name tempName = (Name)otherName;
// eliminate the duplicates when you sort
if(this.getNumber() >tempName.getNumber())
{
return 1;
}else if (this.getNumber() < tempName.getNumber()){
return -1;
}else{
return 0;
}
}
}
I didn't compiled the code, it's edited here so you should fix the code. And also to figure out how to eliminate the duplicates and print only the lowest one.
You need to sweat too.