I'm facing a problem with java generics. My plan is to implement a binary search tree (key & value) with generics with total ordering. I started by creating this KeyValPair and faced the problem of getting the right compareTo method.
public class KeyValPair <V extends Comparable<V>>
implements Comparable<KeyValPair<V>>{
private int key;
private V value;
private KeyValPair<V> leftchild;
private KeyValPair<V> rightchild;
public KeyValPair(int k,V v){
key=k;
value=v;
}
public Comparable<?> getKey(){
return key;
}
public Comparable<?> getValue(){
return value;
}
public void setRightChild(KeyValPair<V> r){
rightchild=r;
}
public KeyValPair<V> getRightChild(KeyValPair<V> r){
return rightchild;
}
public void setLeftChild(KeyValPair<V> l){
leftchild=l;
}
public KeyValPair<V> getLeftChild(KeyValPair<V> l){
return leftchild;
}
#Override
public int compareTo(KeyValPair<V> toComp) {
if(this.getValue().compareTo(toComp.getValue())>0){
return -1;
}else if(this.getValue().compareTo(toComp.getValue())==0){
return 0;
}else{
return 1;
}
}
}
The if sentences in compareTo are not acceppted and I think it's because of the fact that I overrode the compareTo, but how should I compare generics?
Also tried Comparable instead of K with same result.
Best Regards
EDIT: What compiler says: Multiple markers at this line - The method compareTo(capture#1-of ?) in the type Comparable is not applicable for the arguments (Comparable) - Line breakpoint:KeyValPair [line: 39] - compareTo(KeyValPair)
EDIT2:
UPDATED CODE:
public class KeyValPair{
private int key;
private Comparable<?> value;
private KeyValPair leftchild;
private KeyValPair rightchild;
public KeyValPair(int k,Comparable<?> v){
key=k;
value=v;
}
public Comparable<?> getKey(){
return key;
}
public Comparable<?> getValue(){
return value;
}
public void setRightChild(KeyValPair r){
rightchild=r;
}
public KeyValPair getRightChild(KeyValPair r){
return rightchild;
}
public void setLeftChild(KeyValPair l){
leftchild=l;
}
public KeyValPair getLeftChild(KeyValPair l){
return leftchild;
}
}
Now i updated the code of the KEYVALPAIR, but if i test it with my BST Class with method adder as example:
private void adder(KeyValPair current,KeyValPair toInsert) {
if(toInsert.getValue().compareTo(current.getValue())>0){
//dosomething
}
}
it throws:
The method compareTo(capture#2-of ?) in the type Comparable is not applicable for the
arguments (Comparable)
SOLUTION:
I solved it by putting KEYVALPAIR as inner class to BST and use V extends Comparable.
Works now, thanks for your help.
You don't need to cast key or value to comparable since V is required to be comparable already. Doing so just makes it harder to use your class because now you have just a Comparable instead of usable values or keys.
public int getKey() {
return key;
}
public V getValue() {
return value;
}
#Override
public int compareTo(KeyValPair<V> toComp) {
return -this.getValue().compareTo(toComp.getValue());
}
You should also consider to relax the requirement that V must implement Comparable<V>
class KeyValPair<V extends Comparable<? super V>>
would allow e.g. classes like Apple extends Fruit implements Comparable<Fruit> - those can still be ordered if they are comparable to super types.
Your problem here is that you are required to compare V to an other V and you don't have the slightest idea about its runtime type.
It can be a String or an Integer and you obviously don't compare them the same way.
So I think that your KeyValPair should not implement Comparable since its members (V instances) are already doing so: <V extends Comparable<V>>.
If you want some comparison you can simply do something like:
leftChild.getValue().compareTo(rightChild.getValue());
getValue() should also return Comparable<V>, the wildcard is not necessary.
Related
I wanted a simple way to create a Comparator object based on any given Comparable class, so I coded the ComparatorCreator object, which I believed would look at the type of its generic and return the proper Comparator object that I could use to compare objects of that type. So I wrote the following code to test my idea. my belief would be that since the BackwardsInt class' compareto method is a reversed Comparator, it should be able to compare any two numbers in reverse order. But as it went through the statements, it completely ignored the reversal when it was comparing two ints, and even threw an error. I want to know why this is happening. The comparator I created seems to be aware of the type of the Number I'm passing in its arguments.
(I also got really into bounded wildcards a while back, so if that caused the problem, then oops).
import java.util.Comparator;
public class what {
public static void main(String[] ignoreme)
{
Comparator comp = new ComparatorCreator<BackwardsInt>().getComparator();
//comp should represent a Comparator<Number> which redirects to BackwardsInt.compareTo
int big=6;
int small=2;
BackwardsInt bbig=new BackwardsInt(6);
BackwardsInt bsmall=new BackwardsInt(2);
System.out.println(comp.compare(bbig, bsmall));//prints -1 good
System.out.println(comp.compare(bbig, small));//prints -1 good
System.out.println(comp.compare(big, small));//prints 1 why?
System.out.println(comp.compare(big, bsmall));//throws error?!?
}
private static class ComparatorCreator<T extends Comparable<? super T>>{
public Comparator<? extends T> getComparator()
{
return T::compareTo;
}
}
private static class BackwardsInt extends Number implements Comparable<Number>{
private int val;
public BackwardsInt(int v)
{
val=v;
}
#Override
public int compareTo(Number o) {
double d = o.doubleValue()-val;
if(d>0)
return 1;
if(d<0)
return -1;
return 0;
}
public int intValue() {
return val;
}
public long longValue() {
return val;
}
public float floatValue() {
return val;
}
public double doubleValue() {
return val;
}
}
}
Lets look at your ComponentCreator
private static class ComparatorCreator<T extends Comparable<? super T>>{
public Comparator<? extends T> getComparator()
{
return T::compareTo;
}
}
If have slightly changed it to make your mistake more clear
private static class ComparatorCreator<T extends Comparable<? super T>>{
public Comparator<? extends T> getComparator()
{
return (o1, o2) -> {
return o1.compareTo(o2);
};
}
}
The method reference you used is the same as the lambda I have but it makes the mistake more obvious. If we check your samples one by one we can see the following:
comp.compare(bbig, bsmall); // calls bbig.compareTo(bsmall)
comp.compare(bbig, small); // calls bbig.compareTo(small)
comp.compare(big, small); // calls big.compareTo(small)
comp.compare(big, bsmall); // calls big.compareTo(bsmal)
The output you received makes sense because big.compareTo() will call the compareTo() function of the Integer class.
I have this simple node:
public class Node<T> implements Comparable<Node>{
T value;
Node<T> next;
public Node(T value){
this.value = value;
this.next = null;
}
public int compareTo(Node other){
return this.value.compareTo(other.value);
}
}
Eclipse is asking me to cast "this.value". Casting it with int doesnt work. How should it be done?
Your declaration of T does not "extend" Comparable, so you can't use it to compare.
You could change it to:
public class Node<T extends Comparable<T>> implements Comparable<Node<T>>{
T value;
Node<T> next;
public Node(T value){
this.value = value;
this.next = null;
}
public int compareTo(Node<T> other){
return this.value.compareTo(other.value);
}
}
This assumes that T implements Comparable.
Otherwise, if T is not really a comparable, you may just do the comparison at the Node level. But you should probably still declare your class like this
public class Node<T extends Something>
so that you have methods from Something to work with when doing the comparison.
If I go back at the beginning: When you instantiate your Node, you do something like this:
Node<MyType> node = new Node<MyType>();
MyType becomes your T. Is MyType a comparable ? If so, you may declare your class as shown above. Otherwise, you will not be able to do T.compareTo (aka MyType.compareTo), so you need to perform your comparison using other fields from MyType.
I hope this is clear enough..
I have a class that should accept different datatypes as the second constructor parameter:
public abstract class QueryMatch {
String key;
Object input;
public <T> QueryMatch(String key, T o) {
this.key = key;
input = o;
}
public String getKey() {
return key;
}
public Object getValue() {
return input;
}
}
I don't want to use type parameters, like
public abstract class QueryMatch<T>{
String key;
T input;
...
As this way I'm getting raw types warnings when declaring retrieving QueryMatch as a generic (as I don't know the datatype it contains). But the problem is that I need to return the value and I'm not totally comfortable by returning an Object (is that just me, but it doesn't seem like a good practice?).
Additionally, another class inherits from it:
public class QueryMatchOr extends QueryMatch {
public QueryMatchOr() {
super("title", new ArrayList<String>());
}
public void addMatch(String match) {
((ArrayList<String>) input).add(match);
}
}
And of course I'm getting a Unchecked cast warning (which I can avoid with #SuppressWarnings(“unchecked”)).
So, my question is... is there a better way to achieve what I'm trying to do? An abstract class that contains an object (which could be bounded), and returning the datatype it contains (instead of an Object) without using a type parameter in the class declaration?
What you are doing is not a good design. You are using an Object type field from the superclass while you only can know it's actual (needed) type in the subclass. If you only know that in the subclass, declare that variable in the subclass. Not even to mention that your fields are not private.
How about:
public abstract class QueryMatch {
private String key;
public QueryMatch(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public abstract void addMatch(String match);
}
public class QueryMatchOr extends QueryMatch {
private ArrayList<String> input;
public QueryMatchOr() {
super("title");
input = new ArrayList<String>();
}
public void addMatch(String match) {
input.add(match);
}
}
If you need the getValue() method in the superclass, you really should make it generic:
public abstract class QueryMatch<T> {
private String key;
public QueryMatch(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public abstract void addMatch(String match);
public abstract T getValue();
}
public class QueryMatchOr extends QueryMatch<ArrayList<String>> {
private ArrayList<String> input;
public QueryMatchOr() {
super("title");
input = new ArrayList<String>();
}
public void addMatch(String match) {
input.add(match);
}
public ArrayList<String> getValue(String match) {
input;
}
}
So first, I think the best answer is to make your class generic. But if you really don't want to do this you could do something like this:
public <T> T getValue(Class<T> type) {
return (T)input;
}
In some way you need to provide the expected type for the return value to the class. This can either be done my making that class generic or the method generic.
So, my question is... is there a better way to achieve what I'm trying to do?
No, there isn't.
I think you should use generics instead of #SuppressWarnings(“unchecked”))
I have a generic tree class in which each tree node holds some data. Each piece of data has one attribute of the type String. I want to sort each tree node's children alphabetically by this attribute.
The Tree class:
public class Tree<T>{
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
}
Note that the tree's children are of type Tree!
An example actual type parameter for the Tree class is the following:
public class DataItem{
public String name;
}
My idea is to extend the Tree class with a sort() method and use a Comparator like the following but I am stuck at the comparison function:
public class Tree<T>{
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
public void sort(){
Collections.sort(this.children,
new Comparator<Tree<T>>(){
#Override
public int compare(Tree<T> objectA, Tree<T> objectB){
//I am stuck here!
return 0;
}
}
);
for(Tree<T> child: this.children){
child.sort();
}
}
}
I have different ideas to solve this problem:
Use reflection to acces the objects' attributes and compare them.
Implement the interface Comparable in DataItem.
Use a new interface to acces the objects' attribute for comparison:
public interface GetComparisonAttribute {
public String getComparisonAttribute();
}
public class DataItem implements GetComparisonAttribute{
public String name;
#Override
public String GetComparisonAttribute(){
return this.name;
}
}
//the comparison function inside Tree<T>.sort():
public int compare(Tree<T> objectA, Tree<T> objectB){
return objectA.data.getComparisonAttribute()
.compareToIgnoreCase(objectB.data.getComparisonAttribute());
}
What is the right or best thing to do? Are there any other ways?
It may be important to be able to specify the sorting attribute.
I think it would be nice to use Collections.sort() directly on a Tree but implementing it in this recursive data structure really confuses me. A downside of doing it this way is that I cannot specify the sorting attribute.
Try this:
public class Tree<T> {
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
private Class<T> type;
public Tree(Class<T> t) {
type = t;
}
public void sort(){
Collections.sort(this.children,
new Comparator<Tree<T>>(){
#Override
public int compare(Tree<T> objectA, Tree<T> objectB){
if (type==DataItem.class)
{
DataItem diA = (DataItem) (objectA.data);
DataItem diB = (DataItem) (objectB.data);
return diA.name.compareTo(diB.name);
}
else
throw new IllegalArgumentException();
}
}
);
for(Tree<T> child: this.children){
child.sort();
}
}
}
You should pass the type T of the class Tree when you create it. Then you can downcast to DataItem and sort the list according to the filed you like. You can check of course also against other type parameters aside from DataItem.
public void sort(final Comparator<? super T> dataComparator)
{
Collections.sort(this.children,
new Comparator<Tree<T>>()
{
#Override
public int compare(Tree<T> treeA, Tree<T> treeB)
{
return dataComparator.compare(treeA.getData(), treeB.getData());
}
}
);
for(Tree<T> child: this.children)
{
child.sort(dataComparator);
}
}
void test()
{
Tree<DataItem> tree = new Tree<>();
tree.sort(new Comparator<DataItem>()
{
#Override
public int compare(DataItem dataA, DataItem dataB)
{
return dataA.getName().compareTo(dataB.getName());
}
});
}
In java8, this can be simplified as
public void sort(final Comparator<? super T> dataComparator)
{
Collections.sort(this.children,
Comparator.comparing(Tree::getData, dataComparator));
for(Tree<T> child: this.children)
{
child.sort(dataComparator);
}
}
void test()
{
Tree<DataItem> tree = new Tree<>();
tree.sort( Comparator.comparing(DataItem::getName) );
}
How can I create an iterable generic class that runs on two generic types?
That is, if I have a class called:
public class PriorityQueue<K,V> {}
how can I implement Iterable if I am unable to use implements Iterable<K,V>? Eclipse is giving an error saying:
Incorrect number of arguments for type Iterable; it cannot be parameterized with arguments
I must be misunderstanding how to implement my own iterable collection.
And on this subject: Do I want to make my priorityqueue iterable, or do I make my entries that the queue stores iterable?
Edit:
For my homework assignment, I have to implement the PriorityQueue ADT in a linked list fashion. I've implemented all the methods save for one -- min(). The way I'm thinking of doing it is iterating over all the Entry objects stored in my list by making a private entries() method. But I have no idea how to approach this.
I have right now a link to the head of the linked list and a link to the tail. How can I make said entries() method so that I can return an Iterable object of the entries?
Here's my Entry<K,V> object:
public class Entry<K,V> implements Comparable {
private V _value;
private K _key;
private Entry<K,V> _prev;
private Entry<K,V> _next;
public Entry(K key, V value) {
this._value = value;
this._key = key;
this._prev = null;
this._next = null;
}
public V getValue() {
return this._value;
}
public K getKey() {
return this._key;
}
public Entry<K,V> getNext() {
return _next;
}
public void setNext(Entry<K,V> link) {
this._next = link;
}
public Entry<K,V> getPrev() {
return _prev;
}
public void setPrev(Entry<K,V> link) {
this._prev = link;
}
#Override
public int compareTo(Object arg0) {
if (arg0 instanceof Entry<?,?>) {
}
return 0;
}
}
And here's my PriorityQueue<K,V> so far:
public class PriorityQueue<K,V> implements Iterable<K>{
private Entry<K,V> _head;
private Entry<K,V> _tail;
private int _size;
public PriorityQueue() {
this._head = null;
this._tail = null;
this._size = 0;
}
public int size() {
return _size;
}
public boolean isEmpty() {
return (size() == 0);
}
public Entry<K,V> min() {
}
public Entry<K,V> insert(K k, V x) {
Entry<K,V> temp = new Entry<K,V>(k,x);
if (_tail == null) {
_tail = temp;
_head = temp;
}
else {
_tail.setNext(temp);
temp.setPrev(_tail);
_tail = temp;
}
return temp;
}
public Entry<K,V> removeMin() {
Entry<K,V> smallest = min();
smallest.getPrev().setNext(smallest.getNext());
smallest.getNext().setPrev(smallest.getPrev());
return smallest;
}
#Override
public Iterator<K> iterator() {
// TODO Auto-generated method stub
return null;
}
}
You have to use a wrapper class for the returned Iterable object. In your case I assume it to be type Entry. So as an example, your code should look like:
public class PriorityQueue<K, V> implements Iterable<Entry<K, V>> {
}
Of course, you can always create a custom wrapper.
Iterable means you can iterate over objects of its type. It accepts one type parameter.
From the documentation:
public interface Iterable
Implementing this interface allows an object to be the target of the "foreach" statement.
If you want it to be Iterable over your keys your class should implement Iterable<K>.
If you want it to be Iterable over your values your class should implement Iterable<V>.
Here is a blog post about implementing Iterable.
Normally, you want to make the entries that the queue stores iterable, you also might want to figure out in what order.
If you're wandering about implement a Priority Queue you might also want to take a look at Java's own PriorityQueue implementation and follow that.