Do We Have To Overwrite Compare Method In Java?

The compare method in Java doesn’t have to be overwritten to prevent errors, as you can print the memory location without any issues. However, at COMPARE.EDU.VN, we understand that customizing the compare method is about tailoring object comparisons to suit your specific needs, enhancing functionality and clarity. By implementing a custom comparison logic, you gain the power to define exactly how objects are evaluated and ranked, leading to more precise and insightful results. This allows for a string representation instead of just a memory location.

1. Understanding the toString() Method in Java

The toString() method in Java is a fundamental concept in object-oriented programming. It’s a method that belongs to the Object class, which is the parent class of all Java classes. Understanding its purpose and how to override it is crucial for effective Java development.

1.1. What is the toString() Method?

The primary purpose of the toString() method is to provide a string representation of an object. When you print an object to the console or use it in a string concatenation, Java implicitly calls the toString() method of that object to get its string representation.

1.2. Default Implementation

If you don’t override the toString() method in your class, Java uses the default implementation provided by the Object class. This default implementation returns a string consisting of the class name, followed by the “@” symbol, and then the unsigned hexadecimal representation of the hash code of the object. For example:

public class Cat {
}

public class UnderstandingToStringMethod {
    public static void main(String[] args) {
        Cat catObject = new Cat();
        System.out.println(catObject); // Output: Cat@7852e922
    }
}

In this case, the output Cat@7852e922 is not very informative. It tells you that you have a Cat object, but it doesn’t provide any details about the cat itself.

1.3. Why Override toString()?

Overriding the toString() method allows you to provide a more meaningful and useful string representation of your objects. This is particularly important for:

  • Debugging: When debugging your code, you often need to inspect the state of your objects. A well-implemented toString() method can provide a clear and concise view of the object’s data.
  • Logging: When logging information about your application, including object details, a custom toString() method can make the logs more readable and informative.
  • User Interface: When displaying objects in a user interface, you’ll want to present them in a user-friendly format. The toString() method can help you achieve this.

1.4. How to Override toString()

To override the toString() method, you need to:

  1. Add the @Override annotation: This annotation tells the compiler that you are overriding a method from the superclass. It’s optional, but it’s good practice to include it to catch errors if you accidentally misspell the method name or use the wrong signature.

  2. Define the method in your class: The toString() method must have the following signature:

    @Override
    public String toString() {
        // Your implementation here
    }
  3. Return a string representation of your object: Inside the method, you can construct a string that represents the state of your object. This string can include the values of its fields, or any other information that you consider relevant.

1.5. Example of Overriding toString()

Let’s say you have a Cat class with fields for name and age:

public class Cat {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{name='" + name + ''' + ", age=" + age + '}';
    }
}

Now, when you print a Cat object, you’ll get a more informative output:

public class UnderstandingToStringMethod {
    public static void main(String[] args) {
        Cat catObject = new Cat("Whiskers", 3);
        System.out.println(catObject); // Output: Cat{name='Whiskers', age=3}
    }
}

1.6. Best Practices for toString()

  • Include relevant information: The string representation should include the most important information about the object’s state.
  • Use a consistent format: Use a consistent format for your toString() methods across your project. This makes it easier to read and understand the output.
  • Keep it concise: The string representation should be concise and easy to read. Avoid including too much detail, as this can make the output overwhelming.
  • Don’t include sensitive information: Avoid including sensitive information, such as passwords or API keys, in your toString() methods. This information could be exposed in logs or debugging output.
  • Use a builder pattern for complex objects: If your object has many fields, consider using a builder pattern to construct the string representation. This can make the code more readable and maintainable.
  • Consider using a library: Libraries like Apache Commons Lang provide utility classes like ReflectionToStringBuilder that can simplify the process of creating toString() methods.

2. Diving Deeper into Object Representation

When working with objects in Java, it’s essential to understand how they are represented and how you can customize their representation for various purposes. The toString() method is a key tool in this process, but it’s not the only one.

2.1. Object Identity vs. Object Equality

Before diving into more advanced topics, let’s clarify the difference between object identity and object equality.

  • Object Identity: Two objects have the same identity if they are the same object in memory. You can check for object identity using the == operator.
  • Object Equality: Two objects are considered equal if their content is the same. You can define what “equal” means for your objects by overriding the equals() method.

2.2. The equals() and hashCode() Methods

The equals() and hashCode() methods are closely related. If you override equals(), you should also override hashCode() to maintain consistency.

  • equals(): The equals() method determines whether two objects are logically equal. The default implementation in the Object class checks for object identity (i.e., it returns true only if the two objects are the same object in memory). You should override this method to provide a custom definition of equality based on the object’s content.
  • hashCode(): The hashCode() method returns an integer value that represents the hash code of the object. The hash code is used by hash-based data structures like HashMap and HashSet to store and retrieve objects efficiently. If two objects are equal according to the equals() method, they must have the same hash code.

2.3. Why Override equals() and hashCode()?

Overriding equals() and hashCode() is crucial when you want to use your objects in collections like HashMap and HashSet, or when you need to compare objects based on their content rather than their identity.

  • HashMap and HashSet: These collections use the hashCode() method to determine where to store objects, and the equals() method to check for duplicates. If you don’t override these methods, the collections will use the default implementations, which are based on object identity. This can lead to unexpected behavior if you want to store objects that are considered equal based on their content.
  • Logical Equality: When you override equals(), you can define what it means for two objects to be logically equal. This allows you to compare objects based on their content, rather than just their identity.

2.4. Example of Overriding equals() and hashCode()

Let’s go back to our Cat class and override the equals() and hashCode() methods:

import java.util.Objects;

public class Cat {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Cat cat = (Cat) o;
        return age == cat.age && Objects.equals(name, cat.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Cat{name='" + name + ''' + ", age=" + age + '}';
    }
}

In this example, two Cat objects are considered equal if they have the same name and age. The hashCode() method is implemented using the Objects.hash() method, which is a convenient way to generate a hash code based on the object’s fields.

2.5. Best Practices for equals() and hashCode()

  • Follow the contract: Make sure your equals() and hashCode() methods follow the contract defined in the Object class documentation. This contract states that:
    • If two objects are equal according to the equals() method, they must have the same hash code.
    • If two objects have the same hash code, they may or may not be equal according to the equals() method.
    • The equals() method must be reflexive (x.equals(x) must return true), symmetric (x.equals(y) must return true if and only if y.equals(x) returns true), and transitive (if x.equals(y) and y.equals(z) both return true, then x.equals(z) must return true).
  • Use all relevant fields: Include all relevant fields in the equals() and hashCode() methods. If you exclude a field, you may end up with objects that are considered equal even though they have different values for that field.
  • Use the Objects class: The Objects class provides utility methods like Objects.equals() and Objects.hash() that can simplify the implementation of equals() and hashCode().
  • Consider using an IDE: Most IDEs can generate equals() and hashCode() methods automatically. This can save you time and effort, and help you avoid errors.
  • Test your implementation: Make sure to test your equals() and hashCode() methods thoroughly to ensure that they work correctly.

3. The Comparable Interface and the compareTo() Method

In addition to the toString(), equals(), and hashCode() methods, Java provides another important mechanism for working with objects: the Comparable interface and the compareTo() method.

3.1. What is the Comparable Interface?

The Comparable interface is a generic interface that allows you to define a natural ordering for your objects. A class that implements the Comparable interface can be compared to other objects of the same type.

3.2. The compareTo() Method

The Comparable interface defines a single method: compareTo(). This method takes another object of the same type as input and returns an integer value that indicates the relative ordering of the two objects.

  • Negative Value: If the current object is less than the input object, the compareTo() method should return a negative value.
  • Zero: If the current object is equal to the input object, the compareTo() method should return zero.
  • Positive Value: If the current object is greater than the input object, the compareTo() method should return a positive value.

3.3. Why Implement Comparable?

Implementing the Comparable interface allows you to:

  • Sort Objects: You can use the Collections.sort() method or the Arrays.sort() method to sort collections and arrays of objects that implement the Comparable interface.
  • Use Sorted Collections: You can use sorted collections like TreeSet and TreeMap to store objects in a sorted order.
  • Define a Natural Ordering: You can define a natural ordering for your objects, which can be useful in various scenarios.

3.4. Example of Implementing Comparable

Let’s go back to our Cat class and implement the Comparable interface:

public class Cat implements Comparable<Cat> {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Cat other) {
        // Compare by age
        return Integer.compare(this.age, other.age);
    }

    @Override
    public String toString() {
        return "Cat{name='" + name + ''' + ", age=" + age + '}';
    }
}

In this example, the compareTo() method compares Cat objects based on their age. This means that if you sort a collection of Cat objects, they will be sorted in ascending order of age.

3.5. Best Practices for Comparable

  • Be Consistent with equals(): The compareTo() method should be consistent with the equals() method. If two objects are equal according to the equals() method, their compareTo() method should return zero.
  • Consider Multiple Fields: If you need to compare objects based on multiple fields, you can use a chain of comparisons. For example, you can compare by age first, and then by name if the ages are equal.
  • Use the Integer.compare() Method: The Integer.compare() method is a convenient way to compare integer values. It returns a negative value if the first argument is less than the second argument, zero if the arguments are equal, and a positive value if the first argument is greater than the second argument.
  • Document the Ordering: Document the ordering defined by your compareTo() method. This will help other developers understand how your objects are sorted.

4. The Comparator Interface

While the Comparable interface allows you to define a natural ordering for your objects, the Comparator interface provides a more flexible way to define custom orderings.

4.1. What is the Comparator Interface?

The Comparator interface is a functional interface that allows you to define a custom ordering for objects. A class that implements the Comparator interface can be used to compare two objects of the same type.

4.2. The compare() Method

The Comparator interface defines a single method: compare(). This method takes two objects of the same type as input and returns an integer value that indicates the relative ordering of the two objects.

  • Negative Value: If the first object is less than the second object, the compare() method should return a negative value.
  • Zero: If the first object is equal to the second object, the compare() method should return zero.
  • Positive Value: If the first object is greater than the second object, the compare() method should return a positive value.

4.3. Why Use Comparator?

Using the Comparator interface allows you to:

  • Define Multiple Orderings: You can define multiple orderings for the same type of object. This is useful if you need to sort objects in different ways depending on the context.
  • Sort Objects Without Modifying the Class: You can sort objects without modifying the class itself. This is useful if you don’t have control over the class, or if you don’t want to add a natural ordering to the class.
  • Use Lambda Expressions: You can use lambda expressions to create Comparator instances easily. This makes the code more concise and readable.

4.4. Example of Using Comparator

Let’s go back to our Cat class and create a Comparator that compares Cat objects based on their name:

import java.util.Comparator;

public class Cat {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Cat{name='" + name + ''' + ", age=" + age + '}';
    }

    public static Comparator<Cat> NameComparator = new Comparator<Cat>() {
        @Override
        public int compare(Cat cat1, Cat cat2) {
            return cat1.getName().compareTo(cat2.getName());
        }
    };
}

In this example, the NameComparator compares Cat objects based on their name. This means that if you sort a collection of Cat objects using this Comparator, they will be sorted in alphabetical order of name.

4.5. Using Lambda Expressions with Comparator

You can use lambda expressions to create Comparator instances more concisely:

import java.util.Arrays;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        Cat[] cats = {
                new Cat("Whiskers", 3),
                new Cat("Mittens", 2),
                new Cat("Smokey", 5)
        };

        // Sort by name using a lambda expression
        Arrays.sort(cats, (cat1, cat2) -> cat1.getName().compareTo(cat2.getName()));

        // Print the sorted array
        System.out.println(Arrays.toString(cats));
    }
}

In this example, the lambda expression (cat1, cat2) -> cat1.getName().compareTo(cat2.getName()) creates a Comparator that compares Cat objects based on their name. This code is equivalent to the previous example, but it’s more concise and readable.

4.6. Best Practices for Comparator

  • Use Lambda Expressions: Use lambda expressions to create Comparator instances whenever possible. This makes the code more concise and readable.
  • Consider Method References: If you’re using a simple comparison logic, you can use method references to make the code even more concise.
  • Document the Ordering: Document the ordering defined by your Comparator. This will help other developers understand how your objects are sorted.

5. Comparing Different Approaches

Now that we’ve covered the toString(), equals(), hashCode(), Comparable, and Comparator interfaces, let’s compare these different approaches to object representation and comparison.

5.1. toString() vs. equals() and hashCode()

  • toString(): Provides a string representation of an object. Useful for debugging, logging, and user interface display.
  • equals() and hashCode(): Define object equality. Essential for using objects in collections like HashMap and HashSet.

The toString() method is about providing a human-readable representation of an object, while equals() and hashCode() are about defining how objects are compared for equality.

5.2. Comparable vs. Comparator

  • Comparable: Defines a natural ordering for objects. The class itself implements the Comparable interface.
  • Comparator: Defines a custom ordering for objects. A separate class implements the Comparator interface.

The Comparable interface is used when you want to define a default ordering for your objects, while the Comparator interface is used when you want to define multiple orderings or sort objects without modifying the class itself.

5.3. When to Use Which Approach

  • Use toString(): When you need to provide a human-readable representation of your objects.
  • Use equals() and hashCode(): When you need to compare objects for equality, especially when using collections like HashMap and HashSet.
  • Use Comparable: When you want to define a natural ordering for your objects, and you have control over the class.
  • Use Comparator: When you want to define multiple orderings for your objects, or when you don’t have control over the class.

6. Practical Examples and Use Cases

Let’s explore some practical examples and use cases for these concepts.

6.1. Sorting a List of Students by GPA

Suppose you have a Student class with fields for name and GPA. You want to sort a list of students by GPA in descending order.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Student implements Comparable<Student> {
    private String name;
    private double gpa;

    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }

    public String getName() {
        return name;
    }

    public double getGpa() {
        return gpa;
    }

    @Override
    public int compareTo(Student other) {
        // Sort by GPA in descending order
        return Double.compare(other.gpa, this.gpa);
    }

    @Override
    public String toString() {
        return "Student{name='" + name + ''' + ", gpa=" + gpa + '}';
    }

    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 3.8));
        students.add(new Student("Bob", 3.5));
        students.add(new Student("Charlie", 4.0));

        Collections.sort(students);

        System.out.println(students);
    }
}

In this example, the Student class implements the Comparable interface and sorts students by GPA in descending order.

6.2. Sorting a List of Employees by Salary and Then by Name

Suppose you have an Employee class with fields for name and salary. You want to sort a list of employees by salary in ascending order, and then by name in alphabetical order if the salaries are equal.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{name='" + name + ''' + ", salary=" + salary + '}';
    }

    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("Alice", 50000));
        employees.add(new Employee("Bob", 60000));
        employees.add(new Employee("Charlie", 50000));

        Collections.sort(employees, Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName));

        System.out.println(employees);
    }
}

In this example, the Comparator.comparing() method is used to create a Comparator that sorts employees by salary in ascending order. The thenComparing() method is used to create a secondary Comparator that sorts employees by name in alphabetical order if the salaries are equal.

6.3. Using a HashMap to Store Usernames and Passwords

Suppose you want to store usernames and passwords in a HashMap. You need to override the equals() and hashCode() methods in your User class to ensure that users with the same username are considered equal.

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username);
    }

    @Override
    public String toString() {
        return "User{username='" + username + ''' + ", password='" + password + ''' + '}';
    }

    public static void main(String[] args) {
        Map<User, String> users = new HashMap<>();
        User alice = new User("alice", "password123");
        User bob = new User("bob", "password456");

        users.put(alice, alice.getPassword());
        users.put(bob, bob.getPassword());

        System.out.println(users.get(new User("alice", "anypassword")));
    }
}

In this example, the equals() method compares User objects based on their username. The hashCode() method is implemented using the Objects.hash() method, which generates a hash code based on the username.

7. Common Mistakes and How to Avoid Them

When working with object representation and comparison in Java, there are several common mistakes that developers make. Here are some of them, along with tips on how to avoid them:

  • Not Overriding equals() and hashCode() Together: If you override equals(), you must also override hashCode() to maintain consistency. Failing to do so can lead to unexpected behavior when using collections like HashMap and HashSet.
  • Not Including All Relevant Fields in equals() and hashCode(): Include all relevant fields in the equals() and hashCode() methods. If you exclude a field, you may end up with objects that are considered equal even though they have different values for that field.
  • Not Following the equals() Contract: Make sure your equals() method follows the contract defined in the Object class documentation. This contract states that the equals() method must be reflexive, symmetric, and transitive.
  • Using == to Compare Objects: The == operator checks for object identity, not object equality. Use the equals() method to compare objects based on their content.
  • Not Documenting the Ordering Defined by Comparable and Comparator: Document the ordering defined by your Comparable and Comparator implementations. This will help other developers understand how your objects are sorted.
  • Using Inconsistent Logic in compareTo() and equals(): The compareTo() method should be consistent with the equals() method. If two objects are equal according to the equals() method, their compareTo() method should return zero.
  • Ignoring Edge Cases: Always consider edge cases when implementing equals(), hashCode(), compareTo(), and compare(). For example, what happens if one of the objects is null?

8. Advanced Topics

Let’s delve into some more advanced topics related to object representation and comparison in Java.

8.1. Using Reflection to Implement toString()

Reflection is a powerful feature in Java that allows you to inspect and manipulate classes and objects at runtime. You can use reflection to implement a generic toString() method that can be used for any class.

import java.lang.reflect.Field;

public class ReflectionToString {
    public static String toString(Object obj) {
        Class<?> clazz = obj.getClass();
        StringBuilder sb = new StringBuilder(clazz.getSimpleName()).append("{");
        Field[] fields = clazz.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true); // Allow access to private fields
            try {
                sb.append(field.getName()).append("=").append(field.get(obj));
                if (i < fields.length - 1) {
                    sb.append(", ");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        sb.append("}");
        return sb.toString();
    }
}

This code uses reflection to iterate over the fields of an object and append their names and values to a string. This can be a convenient way to implement a toString() method for classes with many fields.

8.2. Using Libraries for equals() and hashCode()

Several libraries provide utility classes that can simplify the implementation of equals() and hashCode(). One popular library is Apache Commons Lang, which provides the EqualsBuilder and HashCodeBuilder classes.

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Cat {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Cat)) {
            return false;
        }
        Cat other = (Cat) obj;
        return new EqualsBuilder()
                .append(name, other.name)
                .append(age, other.age)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(name)
                .append(age)
                .toHashCode();
    }
}

These classes provide a fluent API for building equals() and hashCode() methods. This can make the code more readable and maintainable.

8.3. Using Lombok to Generate Boilerplate Code

Lombok is a library that can automatically generate boilerplate code, such as getters, setters, equals(), hashCode(), and toString() methods. This can significantly reduce the amount of code you have to write.

import lombok.Data;

@Data
public class Cat {
    private String name;
    private int age;
}

The @Data annotation tells Lombok to generate getters, setters, equals(), hashCode(), and toString() methods for the Cat class.

9. Conclusion

In this comprehensive guide, we’ve explored the toString(), equals(), hashCode(), Comparable, and Comparator interfaces in Java. We’ve discussed their purpose, how to implement them, and best practices for using them effectively. We’ve also covered common mistakes to avoid and advanced topics to further enhance your understanding.

Understanding these concepts is crucial for effective Java development. They allow you to:

  • Provide meaningful representations of your objects.
  • Compare objects for equality.
  • Define custom orderings for your objects.
  • Use collections like HashMap and HashSet efficiently.

By mastering these concepts, you’ll be able to write more robust, maintainable, and efficient Java code.

Remember, the key to success is practice. Experiment with these concepts in your own projects and see how they can help you solve real-world problems.

10. FAQs

Here are some frequently asked questions about object representation and comparison in Java:

  1. Why do I need to override equals() and hashCode()?

    • You need to override equals() and hashCode() when you want to compare objects for equality based on their content, especially when using collections like HashMap and HashSet.
  2. What is the difference between == and equals()?

    • The == operator checks for object identity, while the equals() method checks for object equality based on content.
  3. What is the purpose of the toString() method?

    • The toString() method provides a string representation of an object. It’s useful for debugging, logging, and user interface display.
  4. When should I use Comparable vs. Comparator?

    • Use Comparable when you want to define a natural ordering for your objects, and you have control over the class. Use Comparator when you want to define multiple orderings or sort objects without modifying the class itself.
  5. What are some common mistakes to avoid when implementing equals() and hashCode()?

    • Common mistakes include not overriding equals() and hashCode() together, not including all relevant fields, not following the equals() contract, and using inconsistent logic in compareTo() and equals().
  6. Can I use reflection to implement toString()?

    • Yes, you can use reflection to implement a generic toString() method that can be used for any class.
  7. Are there any libraries that can help me implement equals() and hashCode()?

    • Yes, libraries like Apache Commons Lang provide utility classes that can simplify the implementation of equals() and hashCode().
  8. What is Lombok and how can it help me?

    • Lombok is a library that can automatically generate boilerplate code, such as getters, setters, equals(), hashCode(), and toString() methods.
  9. How can I sort a list of objects by multiple fields?

    • You can use the Comparator.comparing() and thenComparing() methods to create a Comparator that sorts objects by multiple fields.
  10. What should I do if I encounter a NullPointerException when comparing objects?

    • Make sure to handle null values properly in your equals(), compareTo(), and compare() methods.

Still struggling to decide which approach is best for your specific scenario? At COMPARE.EDU.VN, we simplify complex decisions by providing detailed comparisons and expert insights.

Whether you’re comparing different sorting algorithms or evaluating object representation methods, our comprehensive guides help you make informed choices.

Ready to make confident decisions? Visit COMPARE.EDU.VN today and discover the power of informed comparison. Our team at COMPARE.EDU.VN is dedicated to providing you with clear, concise, and comprehensive comparisons to help you make the best choices. Contact us at 333 Comparison Plaza, Choice City, CA 90210, United States. Whatsapp: +1 (626) 555-9090 or visit our website at compare.edu.vn for more information.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *