泛型二叉搜索树compareTo()方法类型转换错误修复求助
我正在开发一个泛型二叉搜索树(BST)项目,代码大体已完成,但其中一段代码无法正常运行。
问题代码如下:
private Node<E> add ( E item, Node<E> root ) { if ( root == null ) { return new Node<E>(item); } if ( root.data == item ) { return root; } else if ( root.data.compareTo(item)< 0) { root.right = add ( item, root.right ); } else { root.left = add ( item, root.left); } return root; }
错误出现在第一个else if语句,错误信息为:
class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
测试时我同时输入了整数和字符,想验证二叉树是否支持多种数据类型。我原以为compareTo()可以在字符和整数间通过ASCII值对比,但现在推测问题出在root.data.compareTo(item)这行,想请教如何正确比较root.data实例与item变量?
完整代码如下:
package project4; import java.util.Iterator; public class BST<E extends Comparable<E>> extends Object implements Iterable<E>, Cloneable{ public static void main(String args []){ BST tree = new BST(); tree.add("g"); tree.add(5); tree.add(6); tree.add(2); tree.add(4); tree.add("h"); System.out.println(tree.toStringTreeFormat() ); } // actual class definition private Node<E> root; private E[] elements; public BST() { root = null; } /* BST(E[] collection){ if(collection == null) { throw new IllegalArgumentException("Can not provide a null argument"); } else { } } */ // add function /* public boolean add( int val ) { if (root == null ) { Node n = new Node (val) ; root = n; return true; } return add (val, root); } private boolean add (int val, Node n ) { //found duplicate if ( val == n.data) return false; if ( val < n.data ) { // go left if ( n.left == null ) {//attach it right here Node current = new Node (val); n.left = current; return true; } else { // recurse to its left subtree return add(val, n.left) ; } } else { // go right if ( n.right == null ) {//attach it right here Node current = new Node (val); n.right = current; return true; } else { // recurse to its right subtree return add(val, n.right) ; } } } */ public boolean add ( E e ) { int oldSize = size(); root = add( e, root ); if (oldSize == size()) return false; return true; } private Node<E> add ( E item, Node<E> root ) { if ( root == null ) { return new Node<E>(item); } if ( root.data == item ) { return root; } else if ( root.data.compareTo(item)< 0) { root.right = add ( item, root.right ); } else { root.left = add ( item, root.left); } return root; } // remove function private boolean found ; public boolean remove(E val) { found = false; root = recRemove(val, root); return found; } private Node<E> recRemove(E target, Node<E> node) { if (node == null) found = false; else if (target.compareTo(node.data) < 0) node.left = recRemove(target, node.left); else if (target.compareTo(node.data) > 0) node.right = recRemove(target, node.right ); else { node = removeNode(node); found = true; } return node; } private Node<E> removeNode(Node<E> node) { E data; if (node.left == null) return node.right ; else if (node.right == null) return node.left; else { data = getPredecessor(node.left); node.data = data; node.left = recRemove(data, node.left); return node; } } private E getPredecessor(Node<E> subtree) { Node<E> temp = subtree; while (temp.right != null) temp = temp.right ; return temp.data; } // check if a value is stored in the tree public boolean contains (E val ) { if (root == null) return false; Node<E> cur = root; do { E curVal = cur.data; if (val == curVal) return true; if (val.compareTo(curVal) < 0) cur = cur.left; else cur = cur.right; } while (cur != null) ; return false; } // this should be stored in a data filed to improve performance // calculation is done just as an exercise public int size ( ) { return size(root); } private int size( Node<E> n ) { if (n == null ) return 0; return 1 + size(n.left) + size(n.right); } private static class Node<E>{ E data; Node<E> left ; Node<E> right ; Node( E data ) { this.data = data; } Node(E data, Node<E> l, Node<E> r ) { this.data = data; left = l; right = r; } } private void preOrderPrint(Node<E> tree, int level, StringBuilder output) { if (tree != null) { String spaces = "\n"; if (level > 0) { for (int i = 0; i < level - 1; i++) spaces += " "; spaces += "|--"; } output.append(spaces); output.append(tree.data); preOrderPrint(tree.left, level + 1, output); preOrderPrint(tree.right, level + 1, output); } // uncomment the part below to show "null children" in the output else { String spaces = "\n"; if (level > 0) { for (int i = 0; i < level - 1; i++) spaces += " "; spaces += "|--"; } output.append(spaces); output.append("null"); } } public String toStringTreeFormat() { StringBuilder s = new StringBuilder(); preOrderPrint(root, 0, s); return s.toString(); } @Override public Iterator<E> iterator() { // TODO Auto-generated method stub return null; } }
原始类型误用导致类型校验失效:你在
main方法中创建了BST tree = new BST();,这是一个无泛型约束的原始类型实例,编译器会擦除泛型信息,运行时无法正确校验元素类型。当你先添加String类型的"g",再添加Integer类型的5时,后续调用compareTo方法时,JVM会尝试把Integer强制转换为String(根节点是String类型),直接触发类型转换异常。Comparable接口的泛型限制:
Comparable<E>是泛型接口,String实现的是Comparable<String>,Integer实现的是Comparable<Integer>——这意味着String的compareTo只能接受String参数,Integer的compareTo只能接受Integer参数,跨类型调用必然报错,Java本身不支持String和Integer直接通过compareTo对比ASCII值。
方案1:严格限定单一类型(符合泛型BST设计初衷)
泛型BST的核心设计目标是存储同一种类型的可比较元素,创建实例时必须指定具体类型:
public static void main(String args []){ // 仅存储String类型 BST<String> tree = new BST<>(); tree.add("g"); tree.add("h"); // 或者仅存储Integer类型 // BST<Integer> tree = new BST<>(); // tree.add(5); // tree.add(6); System.out.println(tree.toStringTreeFormat() ); }
方案2:自定义通用比较逻辑(需混合存储时使用)
如果一定要在同一个BST中存储不同类型元素,需要自定义统一的比较规则:
- 定义一个通用接口,让所有待存储类型实现它:
interface ComparableValue { int getComparableValue(); }
- 创建包装类实现该接口,统一比较逻辑:
class StringWrapper implements ComparableValue { private String value; public StringWrapper(String value) { this.value = value; } @Override public int getComparableValue() { // 返回字符串首字符的ASCII值作为比较依据 return value.isEmpty() ? 0 : value.charAt(0); } @Override public String toString() { return value; } } class IntegerWrapper implements ComparableValue { private Integer value; public IntegerWrapper(Integer value) { this.value = value; } @Override public int getComparableValue() { return value; } @Override public String toString() { return value.toString(); } }
- 修改BST的泛型约束与比较逻辑:
public class BST<E extends ComparableValue> extends Object implements Iterable<E>, Cloneable{ // 修改add方法的比较逻辑 private Node<E> add ( E item, Node<E> root ) { if ( root == null ) { return new Node<E>(item); } if (root.data.getComparableValue() == item.getComparableValue()) { return root; } else if (root.data.getComparableValue() < item.getComparableValue()) { root.right = add(item, root.right); } else { root.left = add(item, root.left); } return root; } // 其他涉及比较的方法(如contains、remove)也需要同步修改为使用getComparableValue }
- 测试时使用包装类:
public static void main(String args []){ BST<ComparableValue> tree = new BST<>(); tree.add(new StringWrapper("g")); tree.add(new IntegerWrapper(5)); tree.add(new IntegerWrapper(6)); tree.add(new IntegerWrapper(2)); tree.add(new IntegerWrapper(4)); tree.add(new StringWrapper("h")); System.out.println(tree.toStringTreeFormat() ); }
额外优化点
- 替换对象引用比较:
root.data == item仅能比较对象引用,对于字符串、包装类等,应使用root.data.equals(item)判断内容是否相等。 - 添加null值校验:当前代码未处理
item为null的情况,建议在add方法开头添加if (item == null) throw new IllegalArgumentException("Item cannot be null");,避免空指针异常。
内容的提问来源于stack exchange,提问作者Mr.Moffia

