Sorted List in Java - java

I need to sort the list in java as below:
List contains collection of objects like this,
List list1 = {obj1, obj2,obj3,.....};
I need the final list which has "lowest value" and "repetition of name should avoid".
Ex:
List list1 = {[Nellai,10],[Gujarath,10],[Delhi,30],[Nellai,5],[Gujarath,15],[Delhi,20]}
After Sorting , I need the list like this :
List list1 = {[Nellai,5],[Gujarath,10],[Delhi,20]};
I have 2 Delhi (30,20) in my list. But I need only one Delhi which has lowest fare (20).
How to do that it in java?
Gnaniyar Zubair

If order doesn't matter, a solution is to use a Map[String, Integer], add an entry each time you find a new town, update the value each time the stored value is less than the stored one and then zip all the pairs into a list.

Almost the same as #Visage answer, but the order is different:
public class NameFare {
private String name;
private int fare;
public String getName() {
return name;
}
public int getFare() {
return fare;
}
#Override public void equals(Object o) {
if (o == this) {
return true;
} else if (o != null) {
if (getName() != null) {
return getName().equals(o.getName());
} else {
return o.getName() == null;
}
}
return false;
}
}
....
public Collection<NameFare> sortAndMerge(Collection<NameFare> toSort) {
ArrayList<NameFare> sorted = new ArrayList<NameFare>(toSort.size());
for (NameFare nf : toSort) {
int idx = sorted.getIndexOf(nf);
if (idx != -1) {
NameFare old = sorted.get(idx);
if (nf.getFare() < old.getFare()) {
sorted.remove(idx);
sorted.add(nf);
}
}
}
Collections.sort(sorted, new Comparator<NameFare>() {
public int compare(NameFare o1, NameFare o2) {
if (o1 == o2) {
return 0;
} else {
if (o1.getName() != null) {
return o1.getName().compareTo(o2.getName());
} else if (o2.getName() != null) {
return o2.getName().compareTo(o1.getName());
} else {
return 0;
}
}
}
});
}

I would do it in two stages.
Firstrly sort the list using a custom comparator.
Secondly, traverse the list and, for duplicate entries (which will now be adjacent to each other, provided you worte your comparator correctly), remove the entries with the higher values.

If you want to avoid duplicates, perhaps a class like TreeSet would be a better choice than List.

I would use an ArrayList like this:
ArrayList<Name> listOne = new ArrayList<Name>();
listOne.add(new Name("Nellai", 10);
listOne.add(new Name("Gujarath", 10);
listOne.add(new Name("Delhi", 30);
listOne.add(new Name("Nellai", 5);
listOne.add(new Name("Delhi", 20);
Collection.sort(listOne);
Then create the Name class
class name implements Comparable
{
private String name;
private int number;
public Name(String name, int number)
{
this.name= name;
this.number= number;
}
public String getName()
{
return this.name;
}
public int getNumber()
{
return this.number;
}
public int compareTo(Object otherName) // must be defined if we are implementing //Comparable interface
{
if(otherName instanceif Name)
{
throw new ClassCastException("Not valid Name object"):
}
Name tempName = (Name)otherName;
// eliminate the duplicates when you sort
if(this.getNumber() >tempName.getNumber())
{
return 1;
}else if (this.getNumber() < tempName.getNumber()){
return -1;
}else{
return 0;
}
}
}
I didn't compiled the code, it's edited here so you should fix the code. And also to figure out how to eliminate the duplicates and print only the lowest one.
You need to sweat too.

Related

Comparator. How to sort by age, but if name is "A", move it to the end of list?

I have the next task:
I have object MyObject:
MyObject {
private String name;
private int age;
}
Using the Comparator compare() method implementation, I have to sort the list of MyObject by age, but if the name of MyObject is "A", this element should be moved to the end of the list. The list shouldn't contain 2 elements with "A" name, so we ignore this case.
I've written the next code, but it doesn't work
#Override
public int compare(MyObject myObject, MyObject t1) {
if (myObject.getName().equals("A")) {
return 1;
}else if (myObject.getAge() == t1.getAge()) {
return 0;
} else if (myObject.getAge() > t1.getAge()) {
return 1;
} else {
return -1;
}
}
Actually I don't surprised that it doesn't work, as I don't understand which sorting algorithm is used in this case.
Try something like:
#Override
public int compare(MyObject m1, MyObject m2) {
if (m1.getName().equals("A") && !m2.getName().equals("A")) {
return 1;
}else if (!m1.getName().equals("A") && m2.getName().equals("A")) {
return -1;
} else {
return Integer.compare(m1.getAge(), m2.getAge());
}
}

A Java Set for at most 2 objects

I'm writing a method that returns a Set<String>. The set may contain 0, 1, or 2 objects. The string keys are also quite small (maximum 8 characters). The set is then used in a tight loop with many iterations calling contains().
For 0 objects, I would return Collections.emptySet().
For 1 object, I would return Collections.singleton().
For 2 objects (the maximum possible number), a HashSet seems overkill. Isn't there a better structure? Maybe a TreeSet is slightly better? Unfortunately, I'm still using Java 7 :-( so can't use modern things like Set.of().
An array of 2 strings would probably give the best performance, but that's not a Set. I want the code to be self-documenting, so I really want to return a Set as that is the logical interface required.
Just wrap an array with an AbstractSet. You only have to implement 2 methods, assuming you want an unmodifiable set:
class SSet extends AbstractSet<String> {
private final String[] strings;
SSet(String[] strings) {
this.strings = strings;
}
#Override
public Iterator<String> iterator() {
return Arrays.asList(strings).iterator();
}
#Override
public int size() {
return strings.length;
}
}
If you want, you can store the Arrays.asList(strings) in the field instead of a String[]. You can also provide 0, 1 and 2-arg constructors if you want to constrain the array only to be that length.
You can also override contains:
public boolean contains(Object obj) {
for (int i = 0; i < strings.length; ++i) {
if (Objects.equals(obj, strings[i])) return true;
}
return false;
}
If you don't want to create a list simply to create an iterator, you can trivially implement one as an inner class:
class ArrayIterator implements Iterator<String> {
int index;
public String next() {
// Check if index is in bounds, throw if not.
return strings[index++];
}
public boolean hasNext() {
return index < strings.length;
}
// implement remove() too, throws UnsupportedException().
}
The set is then used in a tight loop with many iterations calling contains().
I would probably streamline it for this. Perhaps something like:
public static class TwoSet<T> extends AbstractSet<T> {
T a = null;
T b = null;
#Override
public boolean contains(Object o) {
return o.equals(a) || o.equals(b);
}
#Override
public boolean add(T t) {
if(contains(t)){
return false;
}
if ( a == null ) {
a = t;
} else if ( b == null ) {
b = t;
} else {
throw new RuntimeException("Cannot have more than two items in this set.");
}
return true;
}
#Override
public boolean remove(Object o) {
if(o.equals(a)) {
a = null;
return true;
}
if(o.equals(b)) {
b = null;
return true;
}
return false;
}
#Override
public int size() {
return (a == null ? 0 : 1) + (b == null ? 0 : 1);
}
#Override
public Iterator<T> iterator() {
List<T> list;
if (a == null && b == null) {
list = Collections.emptyList();
} else {
if (a == null) {
list = Arrays.asList(b);
} else if (b == null) {
list = Arrays.asList(a);
} else {
list = Arrays.asList(a, b);
}
}
return list.iterator();
}
}
You can achieve this by
Make a class that implements Set interface
Override add and remove method
Add value upon class initialisation by super.add(E element)
Use that class instead

Why has my comparator stopped working?

I have a method that looks like this to add and sort:
#Override
public boolean add( Object o )
{
boolean contains = false;
for (Object : this) {
if (o1.getNum().equals(o.getNum())) {
o = o1;
contains = true;
break;
}
}
if (contains) {
// merges
} else {
return //add;
}
// Sorts ArrayList from highest to lowest product number
Collections.sort(this, new Comparator<Object>() {
#Override
public int compare(Object o1, Object o2) {
Integer f = // get number;
Integer s = // get number;
if (f == s) {
return 0;
} else
return f>s ? 1 : -1;
}
});
return true;
}
}
On it's own without any other code, collections.sort works perfectly. However, after adding in the first part, it has stopped working. Any ideas how to get it working again?
I moved my code and placed collection.sort right after return //add;
If a value resulting from a product is not an integer, will Integer.parseInt throw a NumberFormatException?

using Natural order with treeset in java

I am getting an unexpected output for this. Please have a look. I am not able to find the problem. What's wrong with my program? Can anybody explain? I am getting the output
Joe Sue Mike Clare Juliet
Joe Mike Clare Juliet
objects in TreeSets and TreeMaps and with Collections.sort() for Lists, using the Comparable Interface.
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
class Person implements Comparable<Person> {
private String name;
public Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
#Override
public int compareTo(Person person) {
int len1 = name.length();
int len2 = person.name.length();
if(len1 > len2) {
return 1;
}
else if(len1 < len2) {
return -1;
}
else {
return 0;
}
}
}
public class App {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
SortedSet<Person> set = new TreeSet<Person>();
//adding Element
addElements(list);
addElements(set);
//sorting element
Collections.sort(list);
//displaying result
showElements(list);
System.out.println();
showElements(set);
}
//adding element methods
private static void addElements(Collection<Person> col) {
col.add(new Person("Joe"));
col.add(new Person("Sue"));
col.add(new Person("Juliet"));
col.add(new Person("Clare"));
col.add(new Person("Mike"));
}
private static void showElements(Collection<Person> col) {
for(Person element: col) {
System.out.println(element);
}
}
}
You are comparing the persons by the length of their names. And the names "Joe" and "Sue" have the same length. So only one of them can occur in the TreeSet. However, this comparison criterion is not consistent with the implementation of equals!
You should place your Person objects into a list, and sort this list with Collections#sort - preferably, with an own Comparator. Also see https://stackoverflow.com/a/21659849
EDIT: Further explaination:
A Set can contain each element only once. And by the way that you specified your compareTo method, you impled that "Sue" and "Joe" are equal (because their names have equal lengths). So they can not both appear in a Set.
Note: They are not really equal, based on the equals method. But the TreeSet uses the compareTo method, and this compareTo method is currently not consistent with equals. So the Set shows a wrong behavior because of your wrong compareTo method.
EDIT: A possible solution:
If the names have equal lengths, you can compare them alphabetically. This way, the compareTo method becomes consistent with equals: It will return 0 if and only if the names are equal.
#Override
public int compareTo(Person person) {
int len1 = name.length();
int len2 = person.name.length();
if(len1 > len2) {
return 1;
}
else if(len1 < len2) {
return -1;
}
else {
return name.compareTo(person.name);
}
}
A SortedSet uses compareTo for evaluation if an element can be added to the Set. Your input contains two names with same length (your comparison criterion), hence one of Joe or Sue must be filtered out (in your case Sue).
The TreeSet documentation talks about having(not mandatory) compareTo implementaion consistent with equals. In your case 'Joe' and 'Sue' are of equal length and as per your compareTo implementation they are same. One workaround would be to compare the hashcode values in compareTo methods if they are of equal length.

Sorting and comparing XML from Java

I was given XML and schema files. My goal was to output all data from the XML (without duplicates) and order this list by the date of birth. Currently I got all data printed out (with duplicates) and I don't know what to do next. I've tried different things, but unsuccessfully.
HashSet will depend on the Node.equals() method to determine equality, and you're adding distinct nodes, albeit with the same underlying text. From the doc:
adds the specified element e to this set if this set contains no
element e2 such that (e==null ? e2==null : e.equals(e2))
I would extract the underlying text (String) from the Node, and a HashSet<String> will determine uniqueness correctly.
EDIT
After reading the post again I realised I need to remove dups too so:
You can use a TreeSet to impose unqiueness and sort by DOB - I presume that a person with the same first name, surname and date of birth is the same person.
First I would wrap your Node in a class that implements Comparable and that also does the getting of all those properties you have. The wrapper needs to implement Comparable as the TreeSet uses this method to decide whether elements are different (a.compareTo(b) != 0) and also how to order them.
public static final class NodeWrapper implements Comparable<NodeWrapper> {
private static final SimpleDateFormat DOB_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private final Element element;
private final Date dob;
private final String firstName;
private final String surName;
private final String sex;
public NodeWrapper(final Node node) {
this.element = (Element) node;
try {
this.dob = DOB_FORMAT.parse(initDateOfBirth());
} catch (ParseException ex) {
throw new RuntimeException("Failed to parse dob", ex);
}
this.firstName = initFirstName();
this.surName = initSurnameName();
this.sex = initSex();
}
private String initFirstName() {
return getNodeValue("firstname");
}
private String initSurnameName() {
return getNodeValue("surname");
}
private String initDateOfBirth() {
return getNodeValue("dateofbirth");
}
private String initSex() {
return getNodeValue("sex");
}
private String getNodeValue(final String name) {
return element.getElementsByTagName(name).item(0).getTextContent();
}
public Node getNode() {
return element;
}
Date getDob() {
return dob;
}
public String getFirstName() {
return firstName;
}
public String getSurName() {
return surName;
}
public String getDateOfBirth() {
return DOB_FORMAT.format(dob);
}
public String getSex() {
return sex;
}
public int compareTo(NodeWrapper o) {
int c;
c = getDob().compareTo(o.getDob());
if (c != 0) {
return c;
}
c = getSurName().compareTo(o.getSurName());
if (c != 0) {
return c;
}
return getFirstName().compareTo(o.getFirstName());
}
#Override
public int hashCode() {
int hash = 5;
hash = 47 * hash + (this.dob != null ? this.dob.hashCode() : 0);
hash = 47 * hash + (this.firstName != null ? this.firstName.hashCode() : 0);
hash = 47 * hash + (this.surName != null ? this.surName.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NodeWrapper other = (NodeWrapper) obj;
if (this.dob != other.dob && (this.dob == null || !this.dob.equals(other.dob))) {
return false;
}
if ((this.firstName == null) ? (other.firstName != null) : !this.firstName.equals(other.firstName)) {
return false;
}
if ((this.surName == null) ? (other.surName != null) : !this.surName.equals(other.surName)) {
return false;
}
return true;
}
#Override
public String toString() {
return "FirstName: " + getFirstName() + ". Surname: " + getSurName() + ". DOB: " + getDateOfBirth() + ". Sex: " + getSex() + ".";
}
}
So if the date of birth, surname and firstname are all equal we assume it is the same person - we return 0. It is good practice, if using compareTo in this way to make it consistent with equals so that if a.compareTo(b)==0 then a.equals(b), I have added the required equals and hashCode methods as well.
Now you can use a TreeSet in your code which will automatically sort and guarantee unqiueness:
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("file.xml"));
final Set<NodeWrapper> inimesteList = new TreeSet<NodeWrapper>();
final NodeList isa = doc.getElementsByTagName("isa");
for (int i = 0; i < isa.getLength(); i++) {
inimesteList.add(new NodeWrapper(isa.item(i)));
}
final NodeList ema = doc.getElementsByTagName("ema");
for (int i = 0; i < ema.getLength(); i++) {
inimesteList.add(new NodeWrapper(ema.item(i)));
}
final NodeList isik = doc.getElementsByTagName("isik");
for (int i = 0; i < isik.getLength(); i++) {
inimesteList.add(new NodeWrapper(isik.item(i)));
}
System.out.println();
System.out.println("Total: " + inimesteList.size());
for (final NodeWrapper nw : inimesteList) {
System.out.println(nw);
}
I have also added a toString method and used that to print the nodes - this makes the code much cleaner.
The Document approach, while seeming simpler than JAXB, is riddled with this sort of tedium. As you already have a schema I would strongly recommend that you make the move to xjc and JAXB unmarshalling - this will make this sort of stuff hundereds of times easier.
Its better to create a Java Bean (POJO) with the single node details. Override equals() and hashcode() in the same. Store all the Node data into the List of Bean. Then use LinkedHashSet to remove duplicates. Implement Comparable or use Comparator and Collections.sort() to sort the same.
Extend or encapsulate Node in another class and override equals() and hashcode() in the same. Store all the Nodes into the List of new class instance. Then use LinkedHashSet to remove duplicates. Implement Comparable or use Comparator and Collections.sort() to sort the same.

Categories