Can I define my own subclass of java.util.List? - java

Can I define my own list in Java?
I have my own list-type class that is very similar to a LinkedList, called PersonList.
In another program, I'm using a Comparator, so I need to have a List() object as the parameter.
Is it possible to make the following statement, if I make changes in my code?
List list1= new PersonList();
PersonList doesn't extend or import anything.

You'd need to implement the built in interface java.util.List. It would need to define all the methods listed in the interface java.util.List.

You just have to overload the equals function which is implemented by every
class of Type Object (Every class). The list implementation will use your equals implementation due to the polymorphic concept of OOP.
I strongly recommend to use the given List implemenmtations because they meet all
performance issues you don't even think about. When you have concurrency issues refer to the documentation.
In order to achieve customized comparison you have to implement the Comparable interface
and implement its method toCompare(..);
In this way you can use all given Collection API classes and extend them using your own
comparison or equals algorithm which meets your application needs.
Update to to Comments
class Person implements Compareable {
#override
public int compareTo(Person p) {
return p.age > this.age; //Or whatever
}
#Override
equals(Object person) {
if (person instanceof Person) {
Person p = (Person)person;
if (p.x == this.x &&
p.y == this.y &&
p.address.equals(this.address) {
...
return true;
}
}
return false;
}
}
And now just intialize you list.
List<Person> personList = new ArrayList<Person>();
or
List<Persin> personList = new Vector<Person>();
or
LinkedList<Person> personList = new Queue<Person>();
and and and.
Collections.sort(personList);

To answer the question in the comment, "How would I go about writing my own Comparator for a Linked List?":
public class PersonListComparator implements Comparator<LinkedList> {
#Override
public int compare(LinkedList list1, LinkedList list2) {
// something that returns a negative value if list1<list2, 0 if list1 and
// list2 are equal, a positive value if list1>list2.
}
}
See the javadoc for Comparator, especially the text at the top. This explains what could happen if the compare function could return 0 when list1.Equals(list2) is false. It's not necessarily a problem, depending on how you use it.
Note that I'm still assuming you want to compare entire lists (rather than just individual Persons). Based on later comments, it looks like you want to compare Person objects, but provide different ways to compare ("depending on the different parameter being compared"). You could define more than one class that implements Comparator<Person>. Or you could define a class that takes a parameter when you construct the object:
public enum ComparisonType { NAME, AGE, WEIGHT } // whatever
public class ComparePerson implements Comparator<Person> {
private ComparisonType type;
public ComparePerson(ComparisonType type) {
this.type = type;
}
#Override
public int compare(Person p1, Person p2) {
switch(type) {
case NAME:
// return comparison based on the names
case AGE:
// and so on
...
}
}
}
I haven't tested this, so I could have made a mistake, but you get the idea. Hope this helps, but it's still possible I've misunderstood what you're trying to do.

Related

Can I use an object's compareTo method to sort a list which is an attribute of said object?

I have a class, Person, that has an attribute of ArrayList<Person> children;
I want to access and iterate over the children. Sometimes I need to sort the children list by their age, from oldest to youngest, but not always. When I do need to do it, I will need to do it in a number of places with a number of objects, so it makes sense that I don't do it with new Comparators each time.
Would this then be best accomplished by creating a compareTo(Person o) for the Person class, and then when I want to iterate over the sorted list of children in main(), I do something like:
The compareTo Method:
public class Person implements Comparable<Person> {
// This is in the Person class as a method.
#Override
public int compareTo(Person o) {
if (this.age == o.getAge()) {
return 0;
} else if (this.age < o.getAge()){
return 1; // Because we want to go from largest to smallest.
} else {
return -1;
}
}
}
When I need to access each child of the person within the code and access from there:
Collections.sort(person.getChildren());
for (Person child: person.getChildren()){
// Whatever needs to be done.
}
Is my understanding of this right?
You can compare objects this way, but there are two important ways you can improve your approach.
Are people always sorted by age? More often, they're sorted by name. Sometimes they're sorted by height or weight. If there's not a unique inherently correct way to sort objects, it is usually better to have separate Comparators: Comparator<Person> BY_AGE, Comparator<Person> BY_NAME, and so on. You can make these constants (public static final) to avoid recreating them.
Your compareTo provides correct logic, but you can simplify it by using Integer.compareTo. Even better, you can write the comparator like this: Comparator.comparingInt(Person::getAge).
In total, the code would look like this:
public class Person {
public static final Comparator<Person> BY_AGE = Comparator.comparingInt(Person::getAge);
...
}
It doesn't matter what class the children field is a member of. What matters is the type of the children field itself.
Since the field is a list of Person, sorting it requires the Person class to implement Comparable interface, which is exactly what you did so you'd be fine. In fact, you'd be find even if you keep the ArrayList<Person> children field in some other class and not in the Person class itself.

I'm getting ClassCastException even though I overriden compareTo() [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am getting ClassCastException error. This error occurs when I insert the object that is derived from a class I have created.
My code is below:
When I run, I always get ClassCastException error.
Also, the comparator of my class is shown as null in debugger.
I have written a comparator (as far as I know) and overridden necessary methods.
How can I use a Set<> with a class that I have created and use contains() method?
public class Person implements Comparable<Person>
{
int age;
double height;
public Person(int age, double height)
{
this.age = age;
this.height = height;
}
#Override
public int compareTo(Person person)
{
return age - person.age;
}
public boolean equals(Object obj)
{
final Person other = (Person) obj;
if (this.age == other.age)
return true;
return false;
}
public static void main(String[] args)
{
Set<Person> people = new HashSet<>();
Person p1 = new Person(10, 1.00);
Person p2 = new Person(11, 1.10);
Person p3 = new Person(12, 1.20);
Person p4 = new Person(14, 1.40);
people.add(p1);
people.add(p2);
people.add(p3);
people.add(p4);
if(people.contains(12))
System.out.println("contains");
else
System.out.println("does not contain");
}
}
I have managed to get rid of the error. But now, the output is "does not contain".
What I am about to suggest has nothing to do with ClassCastException, which is thrown simply because you are checking contains with an int argument on a set of objects of type Person ... short answer: you can't cast an object to a subclass of which it is not an instance. Even the javadoc says exactly that.
For situations like this, I really like to use Guava predicates. A predicate allows you to apply a boolean condition to any iterable, returning those elements that satisfy the specified condition. In your example, you can define predicates that return the subset of people of whatever age you want.
Predicate<Person> getAgePredicate(final int age) {
return new Predicate<Person>() {
public boolean apply(Person p) { return p.age == age; }
};
}
Set<Person> people = new Hashset<>();
... // populate people
Set<Person> peopleOfAgeTwelve = Sets.filter(people, getAgePredicate(12));
Hope this helps.
I assume that your class Adjacent implements Comparable interface based on your code. And your class contains a method called getID(). So therefore when you override the compareTo() method, you want to make sure that the object comparison makes sense. I'm not sure what your getID() returns. But it seems like integer. So you might want to change the implementation of compareTo() as follows:
#Override
public int compareTo(Adjacent adj) {
return this.getId() - adj.getId();
}
So this way the comparison will return negative/zero/positive depending on the ID comparison of two Adjacent class objects.
Also in your overrided equals() method, the implementation is incorrect because two objects are not necessarily equal even if they have the same hash code. A good example of overriding equals and hashCode methods is given in another SO post. Also, in your case, I think you probably don't even need to override equals method since your class implements Comparable interface already.
Don't call contains with a parameter that's a different type; this behavior is actually documented. The contains() method is a non-generic method for legacy reasons, and it's not safe if you're using a TreeSet. If you implement hashCode() and equals() and switch to a HashSet, your problems will go away.
Do you really need people sorted by age?
Edit: I see what you're trying to do, now. You don't want a Set, you want Map<Integer, Collection<Person>>, or just a single pass loop to look for the given age.
for (Person p : people) {
if (p.age == 12) ...;
}
or
Map<Integer, Set<Person> peopleByAge = new HashMap<Integer, Set<Person>>();
for (Person p : people) {
if (!peopleByAge.contains(p.age)) {
peopleByAge.put(p.age, new TreeSet<Person>();
}
peopleByAge.get(p.age).add(p);
}
if (people.age.containsKey(12)) ...

Deciding to use Comparable or Comparator

My program implements a Product class whose objects contain the following instance variables: name, priority, price, and amount.
I have a LinkedList of Product objects that I need to sort before doing any other operations on the LinkedList.
I want to sort the list first by priority (from lowest to highest). If priority is the same, then look at the price (lowest to highest), then the name (alphabetical order).
I have done a lot of reading about Collections.sort, Comparable, and Comparator. I believe I need to use the Comparable interface and implement a compareTo method. My thinking is that because both priority, price, and name have a "natural" ordering it makes more sense to use Comparable.
public class Product extends ProductBase implements PrintInterface, Comparable<Product>{
private String name;
private int priority;
private int cents;
private int quantity;
// setters and getters
/**
* Compare current Product object with compareToThis
* return 0 if priority, price and name are the same for both
* return -1 if current Product is less than compareToThis
* return 1 if current Product is greater than compareToThis
*/
#override
public int compareTo(Product compareToThis)
}
Then when I want to sort my LinkedList I just call Collections.sort(LinkedList). Before I start writing the code, can you tell me if I am I missing or forgetting anything?
*************UPDATE*******************************
I just created a separate class called ProductComparator with a compare method.
This is part of the LinkedList class..
import java.util.Collections;
public class LinkedList {
private ListNode head;
public LinkedList() {
head = null;
}
// this method will sort the LinkedList using a ProductComparator
public void sortList() {
ListNode position = head;
if (position != null) {
Collections.sort(this, new ProductComparator());
}
}
// ListNode inner class
private class ListNode {
private Product item;
private ListNode link;
// constructor
public ListNode(Product newItem, ListNode newLink) {
item= newItem;
link = newLink;
}
}
}
I am getting the following error from the IDE when I compile.
The method sort(List, Comparator) in the type Collections is not applicable for the arguments (LinkedList, ProductComparator).
Does anyone know why I am getting this error and can point me in the right direction to resolve it?
If there is a "natural" ordering, use Comparable. Rule of thumb for figuring out if the ordering is "natural" is, whether the order of the objects will always be that.
Having said that, the decision whether to use Comparable or Camparator is not the kind of decision you need to think too much about. Most IDEs have refactoring tools which makes the conversion between a Comparable and a Comparator very easy. So if you choose to walk the wrong path now, changing that will not require too much effort.
The order you define here on your Product is very specific and
will probably change in future versions of your program
might be enriched with contextual parameterization
won't cover new features
So it can hardly been said "natural".
I'd suggest to define a constant, for example
public static Comparator<Product> STANDARD_COMPARATOR = new Comparator<Product>() {
public int compare(Product p1, Product p1) {
return ...
}
};
then you'll be able to easily sort anywhere with
Collections.sort(myProductList, Product.STANDARD_COMPARATOR);
Your code will evolve in a better manner as you'll add other comparators.
Just like you should generally prefer composition over inheritance, you should try to avoid defining the behavior of your objects in immutable manner.
If your order was based only on numbers, Comparable would be fine.
However, since your order (sometimes) involves lexical order of text,
a Comparator class is better, since use of Comparable would mean using
String.compareTo which would prevent you from having internationalization.
A separate class which implements Comparator can make use of a
localized Collator for comparing Strings. For instance:
public class ProductComparator
implements Comparator<Product> {
private final Collator collator;
public ProductComparator() {
this(Locale.getDefault());
}
public ProductComparator(Locale locale) {
this.collator = Collator.getInstance(locale);
}
public int compare(Product product1,
Product product2) {
int c = product1.getPriority() - product2.getPriority();
if (c == 0) {
c = product1.getPrice() - product2.getPrice();
}
if (c == 0) {
c = collator.compare(product1.getName(), product2.getName());
}
return c;
}
}
Regardless of whether you go with Comparable or Comparator, it is wise
to make sure Product has an equals method which checks the same
attributes as the comparison code.

Sorting list of objects

I have a list of objects:
List<WorkflowError> workflowErrors = new List<WorkflowError>();
Of which I am wanting to sort alphabetically on the string field errorCode.
I know I have to use
Collections.sort(list,comparator)
and write a custom Comparator:
public class SortByErrorComparator implements Comparator<WorkflowError>
{
// Comparator logic
return 0;
}
I have seen examples of how to do this for a one dimensional list but I can't work out how to begin for a list of objects.
Any help would be appreciated.
You need to implement the compare method.
public class SortByErrorComparator implements Comparator<WorkflowError> {
public int compare(WorkflowError obj1, WorkflowError obj2) {
return obj1.getErrorCode().compareTo(obj2.getErrorCode());
}
}
And then, you can simply do:
Collections.sort(list, new SortByErrorComparator()) ;
In the Java world, generally we do it inline using anonymous inner classes as so:
Collections.sort(list, new Comparator<WorkflowError>() {
public int compare(WorkflowError obj1, WorkflowError obj2) {
return obj1.getErrorCode().compareTo(obj2.getErrorCode());
}
});
In your comparator, you have to explain how the specific object should be sorted.
if (obj.att1 < obj2.att2)
...
do you get it?
When sorting the list the sort implementation will use your comparator to compare individual paisr of objects. So what your comparator needs to do is decide, for a given pair of WorkFlowErrors, which comes 'first'.
from what you've said this sounds like just getting the error code from each one and doing a string compairson.
If you will always want to compare WorkflowError objects in this manner, the easiest way is to implement Comparable<WorkflowError> on WorkflowError itself.
public int compareTo(WorkflowError that) {
return this.getErrorCode().compareTo(that.getErrorCode());
}
That's assuming errorCode implements Comparable.

In Java, How do you quicksort an ArrayList of objects in which the sorting field is multiple layers deep?

Basically, I have a Container class called "Employees" which has in it an ArrayList. This ArrayList contains "Employee" objects, which in turn contain "EmployeeData" objects which in turn contain String objects such as "first" or "last" (which are employee names).
Here's a diagram of the ArrayList structure:
ArrayList[Employee] emps ==> 1:Many ==> Employee emp
Employee emp ==> 1:1 ==> EmployeeData data
EmployeeData data ==> 1:2 ==> String last // A string that contains employee's last name.
How in the world would I perform a quicksort on the ArrayList so that the "Employee" objects in it are in alphabetical order based on the String object "last"? It seems kinda complicated!
Here's a basic design of my classes:
class Employees{
//data:
private ArrayList<Employee> emps = new ArrayList<Employee>();
//Some constructors go here
//Methods to add, remove, toString, etc, go here
public /*output a sorted ArrayList?*/ sort(){
// Some kind of "quicksort" in here to modify or create a new ArrayList sorted by employee's las name...
}
}
class Employee{
//data:
EmployeeData data;
// Some methods to construct and modify EmployeeData data.
}
class EmployeeData{
//data:
String first, last; // I wish to sort with "last". How do you do it?
double payrate, hours;
//...methods...
}
As you can see, those are the classes. I have no idea how to implement "sort" in the "Employees" class so that it sorts the ArrayList by the "last" variable of the "EmployeeData" class.
You can make a comparator, something like:
public class MyComparator implements Comparator<Employee>
{
public int compare(Employee e1, Employee e2)
{
return e1.getData().getLast().compareTo(e2.getData().getLast());
}
}
Then use it to sort the list.
Collections.sort(myList, new MyComparator());
Alternatively, you can use a TreeSet to sort on insertion using this comparator or make the Employee a comparable object to sort using Collections or a SortedSet.
public class Employee implements Comperable<Employee>
{
...
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
...
}
Define Employee implements Comparable<Employee>.
In the compareTo method, dig into the layers and compare the strings you need. Then you can use Collections.sort(), or you can store the data in a SortedSet, which is naturally ordered.
The best practice is to encapsulate the sorting logic in the class stored in the ArrayList, Employee in this case. Implement Comparable by creating a compareTo(Employee) method.
import java.util.*;
public class Employee implements Comparable<Employee> {
public EmployeeData Data;
public Employee(String first, String last)
{
Data = new EmployeeData(first, last);
}
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
public String toString() {
return Data.First + " " + Data.Last;
}
public static void main(String[] args) throws java.io.IOException {
ArrayList list = new ArrayList();
list.add(new Employee("Andy", "Smith"));
list.add(new Employee("John", "Williams"));
list.add(new Employee("Bob", "Jones"));
list.add(new Employee("Abraham", "Abrams"));
Collections.sort(list);
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
System.in.read();
}
}
public class EmployeeData {
public String First;
public String Last;
public EmployeeData(String first, String last)
{
First = first;
Last = last;
}
}
Output:
Abraham Abrams
Bob Jones
Andy Smith
John Williams
Peter DeWeese and others have given you very good answers. You can use
Collections.sort(myList, new MyComparator());
to sort myList using a Comparator you have defined. <=== What the heck does that mean?
In Java, if something implements Comparable (java.lang.comparable) then you can define an order for your elements. It seems like you know what Java Generics are, as you used them to declare your ArrayList as being of type < Employee >. This is awesome, because you can store an Employee object into each entry in the ArrayList. So far so good?
However, if you want to sort objects, first you have to define an order. Since objects can have various properties, maybe I want to sort my employees by ear-size. In this case, I simply tell Java that my class implements Comparable. With generics, I have to specify that it implements Comparable< Employee > because I am defining an order for my Employee objects (peons, minions, whatever).
Peter DeWeese mentioned:
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
and Jason Goemaat mentioned:
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
What the heck does this mean? If I say that my class implements Comparable then I need to define a compareTo function. (An interface is a collection of methods that need to be implemented) The function compareTo defines the order of my elements.
From the Comparable< T> spec:
int compareTo(T o)
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
If I am comparing ear sizes, and let's say I want big ears to come first in my list, then I could (re)define compareTo as:
public int compareTo(Employee e)
{
if (this.earSize > e.earSize) //big ears come first
return -1;
if (this.earSize == e.earSize) //equality
return 0;
else
return 1; // if e.earSize > this.earSize then return 1
}
To answer Steve Kuo's question, we put the keyword this in our comparator because when we call the compareTo method
x.compareTo(y);
the keyword this will refer to x.
You can think of compareTo as being a method of the object x, so when you call x.compareTo(y) you are really saying this.compareTo(y) from within the scope of object x.
We can also look at a String example:
This means that if I want "Medvedev" to come before "Putin" (as 'M' comes before 'P' in the English alphabet) I would have to state that I want compareTo to return -1 when comparing Medvedev to Putin.
String TheMString = "Medvedev";
String ThePString = "Putin";
then the line
TheMString.compareTo(ThePString);
will evaluate to -1.
Now a standard routine such as Collections.sort(list, comparator) will be able to use these values that compareTo returns to figure out the [absolute] order of list. As you may know, sorting is a comparison based operation and we need to know what value is "less than" or "greater than" another value in order to have a meaningful sort.
One big caveat is that if you call compareTo on Strings, it defaults to alphabetical order, so you may simply tell compareTo to return A.compareto(B) and it will make sure the strings are in order.
Normally (well, I should say, in other cases) when redefining the compareTo method, you must explicitly state a neg/zero/pos return value.
I hope that helps.

Categories