ClassCastException when Treeset.add(), despite implementing comparable with compareTo method - java

I have a class Contact that I have displayed below. I want each Contact object to have a list of other Contacts. I chose a TreeSet because I'd like to avoid duplicate Contacts in the same list. My Contact class implements Comparable and has a constructed compareTo() method that compares instance String variables. I understand the compareTo method is used when adding to a TreeSet because added elements are immediately sorted.
When I try to add a Contact object from a Contact[] (see my setContacts() method below) to my TreeSet, I receive a ClassCastException. My system.out messages is:
Exception in thread "main" java.lang.ClassCastException: shared.Contact cannot be cast to java.lang.String
at java.text.Collator.compare(Unknown Source)
at java.util.TreeMap.compare(Unknown Source)
at java.util.TreeMap.put(Unknown Source)
at java.util.TreeSet.add(Unknown Source)
at shared.Contact.setContacts(Contact.java:51) <-- right here is the TreeSet.add()
at server.Server.readInContacts(Server.java:191)
at server.Server.main(Server.java:51)
What I do not understand is that nowhere in any of the classes I have built do I try to convert anything into a String. Below is my entire Contact class.
Does anyone see anything wrong or see why the exception is occuring?
I'd be happy to post more code if someone tells me what else might be relevant. I don't know what else would be important to see. Thanks in advance.
package shared;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.Collator;
import java.util.LinkedList;
import java.util.TreeSet;
public class Contact implements Serializable, Comparable<Contact>{
private static final long serialVersionUID = 1L;
private String name;
private boolean online;
private TreeSet<Contact> contacts;
private ObjectOutputStream oos;
private ObjectInputStream ois;
public Contact(String name){
this(name, null, null);
}
public Contact (String name, ObjectInputStream ois, ObjectOutputStream oos){
this.name = name;
this.ois = ois;
this.oos = oos;
contacts = new TreeSet<Contact>(Collator.getInstance());
}
public String getName(){
return name;
}
public void logIn(ObjectInputStream ois, ObjectOutputStream oos){
this.ois = ois;
this.oos = oos;
}
public ObjectInputStream getObjectInputStream(){
return ois;
}
public ObjectOutputStream getObjectOutputStream(){
return oos;
}
public void setOnlineStatus(boolean status){
online = status;
}
public boolean isOnline(){
return online;
}
public void setContacts(Contact[] contacts){
this.contacts.clear();
for (Contact c: contacts){
this.contacts.add(c); <-- right here is where my exception occurs
}
}
#Override
public int compareTo(Contact c){
return this.name.compareTo(c.getName());
}
public boolean equals(Object o){
Contact c = (Contact)o;
return this.name.equals(c.getName());
}
public void addContact(Contact c){
LinkedList<Contact> list = new LinkedList<Contact>();
for (Contact contact: contacts){
list.add(contact);
}
}
public Contact[] getContacts(){
return contacts.toArray(new Contact[0]);
}
public int hashCode(){
return name.hashCode();
}
public String toString(){
return this.name;
}
}

The answer is right here in Collator.java:
public int compare(Object o1, Object o2) {
return compare((String)o1, (String)o2);
}
It's not immediately obvious to me but why on Earth are you passing in your own Collator?
Are you dealing with Non ascii?
You also have a inconsistency here: compareTo() == 0 SHOULD be the same as equals() especially since in both cases the only thing you seem to care about is 'name'
So, on the one hand you state you want to remove identical entries and thus you use Comparative identity but then you use a Collator which can definitely produce inconsistent results with equals.
If all you want is remove redundancy than skip the Collator and either use HashSet or TreeSet with no Collator
Alternatively if you MUST use Collated names then use a
TreeMap<String,Contact> contacts = new TreeMap<String,Contact>(Collator.getInstance());
contacts.put(contact.getName(),contact);
You can then use:
Collection<Contact> uniqueContacts = contacts.values();

c.getName() return a String and then you call the equals with this.. so a String is passed to equals...
first line of equals try to cast the String into a Contact... Boom
The compare method will call equals on the object it try to compare.

Related

How can I retrieve a value from HashMap with a String when the Key is a custom class object?

I have a HashMap<CustomClass1, ArrayList<CustomClass2>> called map.
Is it possible to get the ArrayList<CustomClass2> using a String contained within CustomClass1? e.g. I would like to say map.get("zone1") and get the matching ArrayList. This is for a school project and I have to use the HashMap described above. This resembles my code:
public class Main {
public static void main(String [] args) {
HashMap<CustomClass1, ArrayList<CustomClass2>> map = new HashMap<CustomClass1, ArrayList<CustomClass2>();
CustomClass1 example = new CustomClass1("zone1");
map.put(example, new ArrayList<CustomClass2>());
//Later in the code where I don't have the objects in scope (except for map)...
ArrayList<CustomClass2> value = map.get("zone1");
}
class CustomClass1 {
private final String name;
//Additional variables
public CustomClass1 (String name) {
this.name = name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object obj) {
...
String test = (String) obj;
if(test.equals(this.name))
return true;
...
}
}
}
So far I have tried overriding the hashCode() and equals() methods in my CustomClass1 so that it uses the String's hashCode. From my understanding, when I call the map.get(obj) method, it calls obj.hashCode() to find the correct bucket and then obj.equals() to see if it is the correct object. I think I am wrong in thinking this, since I keep getting null values as a result and the equals() method in CustomClass1 is never called.
See comments for workaround/solution.

Multiple comparators in a class

I am hard stuck on a problem I cannot find a good answer to. I've found
this one about custom comparators, but it is incomplete:
class YourClass {
static Comparator<YourClass> getAttribute1Comparator() {
return new Comparator<YourClass>() {
// compare using attribute 1
};
}
static Comparator<YourClass> getAttribute2Comparator() {
return new Comparator<YourClass>() {
// compare using attribute 2
};
}
}
That should work, but I don't know how the comparison part works. Here is my class:
package ZVCVolkel_Logic;
import java.util.Comparator;
public class Vliegtuig implements Comparator<Vliegtuig>{
private String naam;
private String type;
private String status;
private Hangaar hangaar;
public Vliegtuig(String naam, String type, String status, Hangaar hangaar){
this.naam = naam;
this.type = type;
this.status = status;
this.hangaar = hangaar;
}
}
Now I need a comparator for status and for Hangaar.getName(). Can someone help?
It is not the one, he has only 1 comparator. I can get that working too but not with 2 different ones in 1 class.
The comparator interface has a method compare return an int value to determine the relation ship between two objects.
It will return:
a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
static Comparator<Vliegtuig> hangaarNameComparator() {
return new Comparator<Vliegtuig>(){
public int compare(Vliegtuig one, Vliegtuig two) {
return one.getHangaar().getName().compareTo(two.getHangaar().getName());
}
}
}
Here you probably want to take care of NullPointerException if getHangaar() or hangaar.getName() return null.
In java 8 you could do this:
Comparator<Vliegtuig> hangaarNameComparator = Comparator.comparing(Vliegtuig::getHagaar,
Comparator.comparing(Hagaar::getName));
In the comparator implementation you need to compare 2 objects. You can refer to most of JDK classes for example, for instance java.lang.Integer.
In your case solution will be to use embedded compactors from objects like this:
Comparator<Vliegtuig> nameComparator = new Comparator<>() {
#Override
public int compare(Vliegtuig o1, Vliegtuig o2) {
return o1.getName().compareTo(o2.getName());
}
}
And you don't need to extend Comparator by the Vliegtuig.

Java: Sorting text file lines

I'm using eclipse and I'm trying to sort a text file with about 40 lines that look like this:
1,Terminator,1984,Schwarzenegger
2,Avatar,2009,Worthington
3,Avengers,2012,Downey
4,Starwars,1977,Hammill
5,Alien,1979,Weaver
I want sort them alphabetically by the second field so that the text file is altered to look like this:
5,Alien,1979,Weaver
2,Avatar,2009,Worthington
3,Avengers,2012,Downey
4,Starwars,1977,Hammill
1,Terminator,1984,Schwarzenegger
I'm fairly certain I should be doing something involving tokenizing them (which I've already done to display it) and a BufferedWriter but I can't for the life of me think of a way to do it by the second or third field and I feel like I'm missing something obvious.
You will first of course need to read a file, which you can learn how to do here.
Java: How to read a text file
This example will provide several ways you may write the file once you have sorted your data.
How do I create a file and write to it in Java?
As for sorting, I recommend creating a class Movie, which would look similar to
public class Movie implements Comparable<Movie> {
private String name;
private String leadActor;
private Date releaseDate;
public Movie(String name, String leadActor, String releaseDate) {
}
#Override
public int compareTo(Movie other) {
}
}
Ill leave it to you fill in the rest of the constructor and compareTo method. Once you have your compareTo method you will be able to call Collections.sort(List list) passing your list of Movie.
Here are some resources on implementing Comparable.
http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
Why should a Java class implement comparable?
Your comparator
class SampleComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
String array1[] = o1.split(",");
String array2[] = o2.split(",");
return array1[1].compareTo(array2[1]);
}
}
Your Sorting
String [] lines= {"1,Terminator,1984,Schwarzenegger",
"2,Avatar,2009,Worthington",
"3,Avengers,2012,Downey",
"4,Starwars,1977,Hammill",
"5,Alien,1979,Weaver"};
List<String> rowList = new ArrayList<String>(Arrays.asList(lines));
Collections.sort(rowList, new SampleComparator());
for (String string : rowList) {
System.out.println(string);
}
Your Output
5,Alien,1979,Weaver
2,Avatar,2009,Worthington
3,Avengers,2012,Downey
4,Starwars,1977,Hammill
1,Terminator,1984,Schwarzenegger
If you have any doubt on this let me know..
The String class has a very helpful static method called "split". All you do is call split and put it in the delimiter and it gives back a String array with the split up string.
Here's an example:
String line = "How,Now,Brown,Cow";
String[] splitLine = line.split(",");
for(String l: splitLine)
{
System.out.println(l);
}
The above code would print the following:
How
Now
Brown
Cow
Hopefully you can use this and adapt it to your problem.
Good luck!
What you want to do is to use java.util.Comparator and Collections.sort. More on this can be found: http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html
Following #Tyler answer. You can have a default implementation in the Movie class and additional sort orders that you can implement by calling Collections.sort(movieList, new MyComparator()); Here comes an example of both.
package com.stackoverflow;
public class Movie implements Comparable<Movie> {
private String name;
private String leadActor;
private String releaseDate;
public Movie(String name, String leadActor, String releaseDate) {
this.name = name;
this.leadActor = leadActor;
this.releaseDate = releaseDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLeadActor() {
return leadActor;
}
public void setLeadActor(String leadActor) {
this.leadActor = leadActor;
}
public String getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(String releaseDate) {
this.releaseDate = releaseDate;
}
#Override
public int compareTo(Movie other) {
return getName().compareTo(other.getName());
}
}
And if you want to make your own comparator called on your collection:
package com.stackoverflow;
import java.util.Comparator;
public class MyComparator implements Comparator<Movie> {
#Override
public int compare(Movie o1, Movie o2) {
return o1.getLeadActor().compareTo(o2.getLeadActor());
}
}
Try like this :--
ArrayList ar=new ArrayList();
String [] arr=new String[10];
int i=0;
try {
Scanner sc=new Scanner(file);
while (sc.hasNextLine())
{
String ss=sc.nextLine();
i=i+1;
arr[i]=ss;
}
ar.add(arr[5]);
ar.add(arr[2]);
ar.add(arr[3]);
ar.add(arr[4]);
ar.add(arr[1]);
System.out.println(ar);
}
This solution uses Java 8 APIs.
You don't really need to have an explicit implementation of Comparator or create a Comparable class. Using Comparator.comparing with lambda we can elegantly sort lines by custom key.
import java.io.IOException;
import java.nio.file.*;
import java.util.Comparator;
import java.util.stream.Stream;
public class FileSortWithStreams {
public static void main(String[] args) throws IOException {
Path initialFile = Paths.get("files/initial.txt");
Path sortedFile = Paths.get("files/sorted.txt");
int sortingKeyIndex = 1;
String separator = ",";
Stream<CharSequence> sortedLines =
Files.lines(initialFile)
.map(s -> s.split(separator))
.sorted(Comparator.comparing(s -> s[sortingKeyIndex]))
.map(s -> String.join(separator, s));
Files.write(sortedFile, sortedLines::iterator, StandardOpenOption.CREATE);
}
}

Comparing dynamic fields of objects using equals and hashCode methods

To compare the different objects of the same class with their contents like jobTitleId, classificationId, deptId & classificationId was to be done and do some manipulations later using Set and Map. I was able to do that by simply overriding the equals and hashCode methods of Object class and was able to fetch the information (like in the following Map).
Map<LocationData, List<LocationData>>
The following is the class I used (its been shown to you so that it can be referred for my problem statement):
LocationData class
package com.astreait.bulkloader;
public class LocationData {
String locId, deptId, jobTitleId, classificationId;
#Override
public boolean equals(Object obj) {
LocationData ld = (LocationData)obj;
return this.deptId.equals(ld.deptId) && this.jobTitleId.equals(ld.jobTitleId) && this.classificationId.equals(ld.classificationId) &&
this.locId.equals(ld.locId);
}
#Override
public int hashCode() {
return deptId.hashCode() + jobTitleId.hashCode() + classificationId.hashCode() +locId.hashCode();
}
}
Problem:
I'm already known to which all fields of this object I need to make the comparison.
i.e I'm bound to use the variables named classificationId, deptId, jobTitleId & locId etc.
Need:
I need to customize this logic such that the fields Names (classificationId, deptId, jobTitleId & locId etc) can be pulled dynamically along with their values. So, as far as my understanding I made use of 2 classes (TableClass and ColWithData) such that the List of ColWithData is there in TableClass object.
I'm thinking what if I override the same two methods equals() & hashCode();
such that the same can be achieved.
TableClass class #1
class TableClass{
List<ColWithData> cwdList;
#Override
public boolean equals(Object obj) {
boolean returnVal = false;
// I need to have the logic to be defined such that
// all of the dynamic fields can be compared
return returnVal;
}
#Override
public int hashCode() {
int returnVal = 0;
// I need to have the logic to be defined such that
// all of the dynamic fields can be found for their individual hashCodes
return returnVal;
}
}
ColWithData class #2
class ColWithData{
String col; // here the jobTitleId, classificationId, deptId, locId or any other more fields info can come.
String data; // The corresponding data or value for each jobTitleId, classificationId, deptId, locId or any other more fields.
}
Please let me know if I'm proceeding in the right direction or I should make some any other approach. If it is ok to use the current approach then what should be performed in the equals and hashCode methods?
Finally I need to make the map as: (Its not the concern how I will make, but can be considered as my desired result from this logic)
Map<TableClass, List<TableClass>> finalMap;
EDIT I have been down voted. So, I made some modifications for my requirements again. (Please help me out solving this)
Using this class ColWithData is kind of ugly. You should be using a Map<String,String> :
package mypack;
import java.util.*;
public class TableClass {
/* HashMap containing your values:
map.put("locId", [data]);
...
*/
public Map<String,String> cwdMap;
public Map<String,String> getCwdMap() {
return cwdMap;
}
public void setCwdMap(Map<String,String> cwdMap) {
this.cwdMap = cwdMap;
}
#Override
public boolean equals(Object obj) {
TableClass tClass = (TableClass) obj;
for(String col: this.cwdMap.keyset()){
if (! tClass.cwdMap.get(col).equals(this.cwdMap.get(col)){
return false;
}
}
return true;
}
#Override
public int hashCode() {
int hCode = 0;
for(String col: this.cwdMap.keyset()){
hCode = hCode+cwdMap.get(col).hashCode();
}
return hCode;
}
}
In this code I never check for null values but your probably should.
There is another thing that confuse me in your code:
why use getter/setter if your property (cwdList) is public?
I think I have found the solution and its working for me.
Please let me know if there could be the simple or any other way out finding the solution for this problem.
The code snippet is:
package mypack;
import java.util.*;
public class TableClass {
public List<ColWithData> cwdList;
public List<ColWithData> getCwdList() {
return cwdList;
}
public void setCwdList(List<ColWithData> cwdList) {
this.cwdList = cwdList;
}
#Override
public boolean equals(Object obj) {
TableClass tClass = (TableClass) obj;
boolean returnVal = true;
for(ColWithData cwd: this.getCwdList()){
for(ColWithData innerCwd: tClass.getCwdList()){
if(cwd.getCol().equalsIgnoreCase(innerCwd.getCol())){
if(!cwd.getData().equalsIgnoreCase(innerCwd.getData()))
returnVal = false;
}
}
}
return returnVal;
}
#Override
public int hashCode() {
int hCode = 0;
for(ColWithData cwd: this.getCwdList()){
hCode = hCode+cwd.getData().hashCode();
}
return hCode;
}
}
And finally made a map as said:
Map<TableClass, List<TableClass>> map = new LinkedHashMap<TableClass, List<TableClass>>();
displaying the things as desired.

Getting NotSerializableException when trying to write object

Trying to serialize and send Lot object to socket. Getting error:
java.io.NotSerializableException: com.server.ClientServiceThread
Why?
public class ClientServiceThread extends Thread {... // form here called sendObj ...}
public class FlattenLot {
public void sendObj(){
try {
out = new ObjectOutputStream(oStream);
out.writeObject(lot); // error
out.flush();
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
Lot class:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Calendar;
public class Lot implements Serializable{
private static final long serialVersionUID = 1L;
public ArrayList<ClientServiceThread> clientBidsLog = new ArrayList<ClientServiceThread>();
public ArrayList<Integer> bidLog = new ArrayList<Integer>();
private List<Integer> bids = new ArrayList<Integer>();
private List<ClientServiceThread> clients = new ArrayList<ClientServiceThread>();
private String NAME;
private int INITIAL_PRICE;
private int MAX_BID = 0;
public volatile boolean notAvailable = false;
Lot(String name, int initPrice){
NAME = name;
INITIAL_PRICE = initPrice;
}
public synchronized String getName(){return NAME;}
public synchronized int getInitPrice(){return INITIAL_PRICE;}
public synchronized void subscribe(ClientServiceThread t){
clients.add(t);
}
public synchronized void unsubscribe(ClientServiceThread t){
clients.remove(t);
}
public synchronized boolean makeBid(ClientServiceThread t,int i){
if(i > INITIAL_PRICE && i > MAX_BID){
clientBidsLog.add(t);
bidLog.add(i);
bids.add(i);
MAX_BID = i;
t.LAST_BID = i;
notifyAllSubscribers("New bid: "+this.getMaxBid()+" made by "+this.clientBidsLog.get(this.clientBidsLog.size()-1).CLIENT_NAME);
return true;
}else{
return false;
}
}
public synchronized void notifyAllSubscribers(String msg){
for (ClientServiceThread client : clients){
client.lotUpdated(this, msg);
}
}
public synchronized int getMaxBid(){return MAX_BID;}
private Date time;
public Lot() {
time = Calendar.getInstance().getTime();
}
public Date getTime() {
return time;
}
}
The error is caused by trying to serialize a ClientServiceThread, which is not serializable. Somehow one of those is part of a Lot. If Lot is not declared with a ClientServiceThread field (or with a field that contains a ClientServiceThread), then another possibility is that Lot is a non-static inner class of a class that does have such a field. The outer class instance would then be a (hidden) member of Lot.
The solution is to either make ClientServiceThread serializable (not likely, from its name) or else eliminate it from the serialization by marking the relevant field(s) transient (or removing them from the Lot class).
Lot contains
public ArrayList<ClientServiceThread> clientBidsLog
private List<ClientServiceThread> clients
If you wish this field to be serialized mark the ClientServiceThread serializable too
if you don't want it to be serialized just mark it transient like
public transient ArrayList<ClientServiceThread> clientBidsLog
private transient List<ClientServiceThread> clients
A couple of answers have suggested that you could declare ClientServiceThread serializable as a possible solution.
WARNING - that probably won't work!
Yes, you can declare a Thread subclass that implements Serializable, but the Java serialization mechanism can't serialize the stack of a live thread. In fact, I don't even think it will succeed in serializing an inactive thread's state (e.g. the thread's ThreadGroup reference), so you'll probably end up the more exceptions.
I think your only option is to exclude the threads from serialization by declaring those collections to be transient.

Categories