I have to add User identified by his id into set and in runtime all users form that set have to be sorted by this id.
I've created TreeSet added some User objects and tried to iterate through it.
Here is my attempt:
//irrelevant code removed
TreeSet<User> userSet = new TreeSet<User>();
userSet.add(new User(2));
userSet.add(new User(1));
userSet.add(new User(3));
Iterator<User> iterator = userSet.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
I wrote class User where is one of the fields id and constructor have id as parameter.
public class User {
private int id;
// irrelevant code removed
public User(int id) {
this.id = id;
}
// irrelevant code removed
public String toString() {
return id + "";
}
}
When i run this code I get ClassCastException.
Here is the stacktrace:
Exception in thread "main" java.lang.ClassCastException: OrderedUsers$User cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at OrderedUsers.<init>(OrderedUsers.java:9)
at Main.main(Main.java:6)
What I am doing wrong?
You are on the right way when you decided to use TreeSet because with TreeSet you can get ordered output. But...
Note that if you use TreeSet, because of TreeSet is sorted you have to implement Comparable.
When you implement Comparable you will get what you expected.
I suggest that you perform changes like this:
public class User implements Comparable<User> {
private int id;
// irrelevant code removed
public User(int id) {
this.id = id;
}
// irrelevant code removed
public String toString() {
return id + "";
}
#Override
public int compareTo(User u) {
return id - u.id;
}
}
Either pass a custom Comparator to TreeSet constructor or implement Comparable in your model class
TreeSet maintains sorted order and it needs to know how Users can be compared
Here's the statement at TreeMap.java 1188:
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
So if comparator is null, then it will try to cast the set member to a (Comparable). If your class doesn't implement Comparable, this will cause the ClassCastException you saw. comparator is non-null only if you call the TreeMap constructor that provides a Comparator (or if you copy it from another SortedMap that already has a comparator).
TreeSet internally stores the object by performing comparasions with the existing ones similar to Binary Search Tree (actually its a Red-Black tree). So you need to implement interface Comparable in User or provide a custom Comparator to the TreeSet.
If you dont want the user objects to be stored in a sorted order I would recommend using ArrayList.
Method 1 :
public class User implements Comparable<User>
{
public int compare(User u)
{
if( u == null)
return 1;
return id - u.id;
}
}
Method 2 :
public class CompareUsers implements Comparator<User>
{
public int compareTo(User a, User b)
{
if(a == null)
return -1;
if(b == null)
return 1;
return a.id - b.id;
}
}
// Create an instance of this comparator class and pass to the TreeSet
// during initialization.
TreeSet<User> userSet = new TreeSet<User>(new CompareUsers());
Related
public class Organization {
protected List<Address> addresses;
protected String SourceId;
protected String organizationId;
protected String organizationName;
}
public class ReferenceOrg{
public List<Organization> getAllOrgs() {
List<Organization> listOfOrgsTemp = new ArrayList<Organization>();
List<Organization> listOfOrgs = new ArrayList<Organization>();
listOfOrgsTemp.addAll(webToolWS.getAllProviders());
listOfOrgsTemp.addAll(webToolWS.getAllVendors());
for(Organization org : listOfOrgsTemp) {
listOfOrgs.add(org);
}
return listOfOrgs;
}
}
I wanted to sort listOfOrgs by organizationname, but I cannot implement comparator on my organisation class , it is used by different projects and they want to sort on differnt field. with out modifiying organisation class how can i sort listofOrgs list manually.enter code here
I just learned how to use a Comparator to sort a JTable with numeric string data with commas. I didn't try to execute my addition to your code (and had to delete some of your references to get compilation) but maybe this will help get you started. Lines flagged with or surrounded by // ******* show code to add.
public class ReferenceOrg
{
static Comparator compareOrgs; // ***********************
public List<Organization> getAllOrgs()
{
// ************************************************************
compareOrgs = (Comparator) new Comparator()
{
#Override public int compare(Object oo1, Object oo2)
{
Organization or1 = (Organization)oo1;
Organization or2 = (Organization)oo2;
String o1 = or1.organizationId;
String o2 = or2.organizationId;
return o1.compareTo(o2);
}
};
// ************************************************************
List<Organization> listOfOrgsTemp = new ArrayList<Organization>();
List<Organization> listOfOrgs = new ArrayList<Organization>();
...
for(Organization org : listOfOrgsTemp) {
listOfOrgs.add(org);
}
Collections.sort(listOfOrgs, compareOrgs); // *******************
return listOfOrgs;
}
From Javadoc:
java.util.Collections
public static <T> void sort(List<T> list, Comparator<? super T> c)
Sorts the specified list according to the order induced by the specified comparator.
All elements in the list must be mutually comparable using the specified comparator
(that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the list).
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
The specified list must be modifiable, but need not be resizable.
Implementation Note:
This implementation defers to the List.sort(Comparator) method using the specified list and comparator.
Parameters:
list - the list to be sorted.
c - the comparator to determine the order of the list. A null value indicates that the elements'
natural ordering should be used.
I need a "Container" for a few objects. Definition:
class DataSet implements Comparable {
public int id;
public String Date;
public double Value
public DataSetFSA (int id, String Date) {
this.id=id;
this.Date=Date;
}
#Override
public int compareTo(Object o) {
return 1;
}
}
The objects have a certain order, which should not be destroyed! As you see my container class already has an "id" itself!
So my question is now should i insert the object as a key or a value into the TreeMap?
DataSet ds1 = new DataSetFSA(1,"Val1");
DataSet ds2 = new DataSetFSA(2,"Val2");
...
TreeMap DataTreeMap = new TreeMap();
DataTreeMap.put(1,ds1); //as value
DataTreeMap.put(2,ds2);
//or as key (then the class has to implement comparable)
DataTreeMap.put(ds1,1); //as key but then the value doesnt has a function anymore
DataTreeMap.put(ds2,2);
If you're just looking for a sorted (unique) container, there's no reason to use a TreeMap - a TreeSet would seem to fit the bill perfectly.
Pre-TL;DR: Need to see if a comparator is an instance of (or equal to) another comparator, even if it is reversed.
Hello, I think this problem is very simple but I am just thinking too much into it.
I have a program that allows a user to sort lists by different comparators. The user can choose ascending or descending order. Since comparators are by default in ascending order, all I do to achieve descending order is create a new reversed comparator:
Comparator newReversedCMP = Collections.reverseOrder(Comparator originalComp)
Another part of the program saves the last comparator used so that the user can sort or view the last comparator used. Let's say I have comparators dealing with names and salaries and a method that returns what it deals with, for example:
if (lastCMPUsed.equals(new NameComparator()){
return "Name";
}
if (lastCMPUsed.equals(new SalaryComparator()){
return "Salary";
}
Sorry for the long intro but my issue is that I also want to return "Name" or "Salary" even if it's a reverse of the original Name or Salary comparator. If lastCMPUsed is a reversedOrder CMP, then it does not find them equal.
I have tried this but with no success:
//lastCMPUsed = Collections.reversedOrder(new NameComparator());
if (lastCMPUsed.equals(Collections.reverseOrder(new NameComparator())
return "Name";
This does not return "Name" and sees them as not equal.
My equals method in each comparator is a simple instanceof check.
Thanks for reading.
If you need some particular definition of equality besides one defined by the supertype, you should write a class structure that will properly do this for you.
For example:
public abstract class PersonComparator implements Comparator<Person> {
private final boolean isReversed;
private PersonComparator(boolean isReversed) {
this.isReversed = isReversed;
}
public boolean isReversed() {
return isReversed;
}
#Override
public int compare(Person lhs, Person rhs) {
return isReversed ? compareImpl(rhs, lhs) : compareImpl(lhs, rhs);
}
public abstract String getComparison();
protected abstract int compareImpl(Person lhs, Person rhs);
public static final class Name extends PersonComparator {
private Name(boolean isReversed) { super(isReversed); }
#Override
protected int compareImpl(Person lhs, Person rhs) {
return lhs.getName().compareTo(rhs.getName());
}
#Override
public String getComparison() {
return "Name";
}
}
public static final Name NAME = new Name(false);
public static final Name NAME_REVERSE = new Name(true);
}
The equality here is that:
NAME.getClass() == NAME_REVERSE.getClass() // true
NAME instanceof PersonComparator.Name //
&& // true
NAME_REVERSE instanceof PersonComparator.Name //
NAME == NAME_REVERSE // false
NAME.equals(NAME_REVERSE); // false
NAME.getComparison() //
.equals(NAME_REVERSE.getComparison()) // true
Note that I did not need to override equals and hashCode, because there is only a single instance of each possible Comparator.
I have created a Vector object to store data in Table object as Vector<Table>. Vector<Table> contains components as below.
[Vector<Record> records, String tableName, String keyColumnName, int recordCount, int columnCount]
I need to sort tableName in above Vector to my own order and return Vector<Table> with sorted tableNames for other processes.
I have wrote method as below.
private Vector<Table> orderTables(Vector<Table> loadTables) {
List<String> tableNames = new ArrayList<String>();
for (Table table : loadTables) {
String tblName = table.getTableName();
tableNames.add(tblName);
}
Collections.sort(tableNames, new MyComparable());
return null;
}
But I have no idea about how to write Comparator to this. My own sort order is stored in .properties file. I can read it and get value. But I have no idea about how to compare it.
How could I do it?
Before clarification
You need to write a Comparator for Table objects that delegates to the tableName's comparator:
new Comparator<Table>() {
#Override public int compare(Table one, Table two) {
return one.getTableName().compareTo(two.getTableName());
}
}
Note that this will consider Tables that have the same name to be equal. This can mess things up if you put these tables in a HashMap or HashSet. To avoid this, you can detect this case and return one.hashCode() - two.hashCode() if the table names are the same.
Guava's ComparisonChain is a convenient way to write such multi-stage comparisons:
new Comparator<Table>() {
#Override public int compare(Table one, Table two) {
return ComparisonChain.start()
.compare(one.getTableName(), two.getTableName())
.compare(one.hashCode(), two.hashCode())
.result();
}
}
After clarification
Okay, the question is to impose a predefined sorting order rather than sorting the Tables by name. In that case, you need to make a Comparator that is aware of the ordering defined in the .properties file.
One way to achieve this is to initialize a mapping of table names to sorting order indices, and refer that mapping during the comparison. Given the property value:
SORT_ORDER = SALES,SALE_PRODUCTS,EXPENSES,EXPENSES_ITEMS
The mapping should look like:
{
SALES: 0,
SALE_PRODUCTS: 1,
EXPENSES: 2,
EXPENSES_ITEMS: 3
}
Here's what the comparator would look like:
private static class PredefinedOrderComparator implements Comparator<Table> {
public PredefinedOrderComparator() {
// Initialize orderIndex here
}
private final Map<String, Integer> orderIndex;
#Override public int compare(Table one, Table two) {
return orderIndex.get(one.getTableName()) - orderIndex.get(two.getTableName());
}
}
To populate orderIndex from the property value, you need to:
Get the comma-separated list using getProperty() as you mentioned
Split that value on comma (I recommend using Guava's Splitter, but String.split or others will work too)
Initialize a new HashMap<String, Integer> and an int index = 0
Iterate through the split tokens, map the current token to index and increment index
Note the implicit assumption that none of the table names have a comma in it.
public class MyComparable implements Comparator<Table>{
#Override
public int compare(Table table1, Table table2) {
return (table1.getTableName().compareTo(table2.getTableName());
}
}
make sure that you have overridden the hashcode and equals in Table class to achieve this.
I wrote you a very simple example on how to work with a Comparator. If you create a class called Main, copy paste below contents in it, compile and run it, you can see what's going on.
A comparator just needs to implement an interface. For this it needs to implement one method (public int compare(T arg0, T arg1). There you specify how a collection will get sorted; in this case according to the alfabet.
I hope this helps you.
import java.util.*;
public class Main {
public static void main(String[] args) {
System.out.println("Start\n");
List<Item> items = new ArrayList<Item>();
for(String s : new String[]{"mzeaez", "xcxv", "hjkhk", "azasq", "iopiop"}) {
items.add(createItem(s));
}
System.out.println("Items before sort:");
System.out.println(Item.toString(items));
Collections.sort(items, new ItemComparator());
System.out.println("Items after sort:");
System.out.println(Item.toString(items));
System.out.println("End");
}
private static Item createItem(String s) {
Item item = new Item();
item.setS(s);
return item;
}
}
class Item {
private String s;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
#Override
public String toString() {
return "Item: " + s;
}
public static String toString(Collection<Item> items) {
String s = "";
for(Item item : items) {
s += item + "\n";
}
return s;
}
}
class ItemComparator implements Comparator<Item> {
#Override
public int compare(Item item1, Item item2) {
return item1.getS().compareTo(item2.getS());
}
}
Can I contain two different types in a collection? For example, can I have List< String U Integer > ?
Short answer? No. You can (of course) have a List of Objects, but then you can put anything in it, not just String or Integer objects.
You could create a list of container objects, and that container object would contain either an Integer or String (perhaps via generics). A little more hassle.
public class Contained<T> {
T getContained();
}
and implement Contained<Integer> and Contained<String>.
Of course, the real question is why you want to do this? I would expect a collection to contain objects of the same type, and then I can iterate through and perform actions on these objects without worrying what they are. Perhaps your object hierarchy needs further thought?
Nope. You have a couple of alternatives, though:
You can use a List < Object > and stash whatever you like; or
You can use a List < Class-with-2-members > and put your data in one of those class members.
EDIT: Example.
class UnionHolder {
public String stringValue;
public int intValue;
}
List < UnionHolder > myList
...
Of course you'll need a bit of additional code to figure out which kind of data to pull out of the UnionHolder object you just got out of your list. One possibility would be to have a 3rd member which has different values depending on which it is, or you could, say, have a member function like
public boolean isItAString() { return (this.stringValue != null }
If you are doing something like functional programming in Java 8 or above, you may want to try JavaSealedUnions:
Union2.Factory<String, Integer> factory = GenericUnions.doubletFactory();
Union2<String, Integer> strElem = factory.first("hello");
Union2<String, Integer> intElem = factory.second(3);
List<Union2<String, Integer>> list = Array.asList(strElem, intElem);
for (Union2<String, Integer> elem : list) {
elem.continued(
strElem -> System.out.println("string: " + strElem),
intElem -> System.out.println("integer: " + intElem));
}
Haven't tested this, but I think you got the idea.
In addition to the nice answers already provided ...
Possibly, you have the two data types in your algorithm. But you may not have to put them in the same list...
Creating two typed lists could be the clearer for your algorithm, you would still keep the "type-safeness" and carry all your data. Two code samples follow, the second grouping the two lists in a MyData object.
public class Algorithm1 {
public void process(List<String> strings, List<Integer> integers) {
...
}
}
--------------------------------------
public class DataPair {
public List<String> strings;
public List<Integer> integers;
}
public class Algorithm2 {
public void process(DataPair dataPair) {
...
}
}
what you're decribing is the perfect use case for the Visitor pattern
100% statically type-checked
doesn't need Java 8 or above
usage:
List<UnionType> unionTypes = Arrays
.asList(new StringContainer("hello"), new IntegerContainer(4));
for (UnionType unionType : unionTypes) {
unionType.when(new UnionType.Cases<Integer>() {
#Override
public Integer is(StringContainer stringContainer) {
// type-specific handling code
}
#Override
public Integer is(IntegerContainer integerContainer) {
// type-specific handling code
}
});
}
boilerplate code:
interface UnionType {
<R> R when(Cases<R> c);
interface Cases<R> {
R is(StringContainer stringContainer);
R is(IntegerContainer integerContainer);
}
}
class StringContainer implements UnionType {
private final String value;
public StringContainer(String value) { this.value = value; }
public String getValue() { return value; }
#Override
public <R> R when(Cases<R> cases) {
return cases.is(this);
}
}
class IntegerContainer implements UnionType {
private final Integer value;
public IntegerContainer(Integer value) { this.value = value; }
public Integer getValue() { return value; }
#Override
public <R> R when(Cases<R> cases) {
return cases.is(this);
}
}
No. Think about it this way: with generics, the whole idea is to provide type safety. That would not be possible if you could put Objects of different types into it.
You can use the non-generic java.util.List for your purpose.
If you want to ensure that only String or Integer objects enter the list, you could create your own List implementation like so:
public class MySpecialList {
private List list= new LinkedList();
...
public void add(final String string) {
list.add(string);
}
public void add(final Integer integer) {
list.add(integer);
}
...
// add rest of List style methods
}
Drawback: you loose the List interface clarity...