Mapping fields of List to fields of a single Object - java

Suppose, I have a List of cats like this:
[Cat[name="Minnie", age=3], Cat[name="Pixie", age=1], Cat[name="Kazy", age=5]]
And an Object Cats with fields:
class Cats {
int MinnieAge;
int PixieAge;
int KazyAge;
}
What is the best way to map the ages to this Object? Preferably avoiding imperative approach and keeping it nice and clean. Is this possible with MapStruct or the streams API?

If you want to use streams you can start from implementing a collector:
public class CatsCollector implements Collector<Cat, Cats, Cats> {
#Override
public Supplier<Cats> supplier() {
return () -> new Cats();
}
#Override
public BiConsumer<Cats, Cat> accumulator() {
return (cats1, cat) -> {
switch (cat.name){
case "Minnie": cats1.minnieAge = cat.age; break;
case "Pixie": cats1.pixieAge = cat.age; break;
case "Kazy": cats1.kazyAge = cat.age; break;
}
};
}
#Override
public BinaryOperator<Cats> combiner() {
return (cats1, cats2) -> cats1;
}
#Override
public Function<Cats, Cats> finisher() {
return cats1 -> cats1;
}
#Override
public Set<Characteristics> characteristics() {
return Set.of(Characteristics.UNORDERED);
}
}
P.S. - Here a combiner is more like a stub because there are no requirements for combining logic provided.
P.P.S - There are also sort of assumptions to simplify access modifiers stuff.
Then if you have the model like this:
public class Cats {
int minnieAge;
int pixieAge;
int kazyAge;
#Override
public String toString() {
return "Cats{" +
"minnieAge=" + minnieAge +
", pixieAge=" + pixieAge +
", kazyAge=" + kazyAge +
'}';
}
}
You can use your custom collector:
public static void main(String[] args) {
List<Cat> cats = List.of(
new Cat("Minnie", 3),
new Cat("Pixie", 1),
new Cat("Kazy", 5));
Cats catsResult = cats.stream().collect(new CatsCollector());
System.out.println(catsResult);
}

I would define a Cat class with the attribute name and age. Then simply parse the input string into cat objects and store them in a list which already supports stream. See below example:
import java.util.*;
class Cat {
public String name;
public int age;
private Cat(String name, int age) { this.name = name; this.age = age;}
public String toString(){
return String.format("Cat[name=\"%s\", age=%d]",name,age);
}
public static List<Cat> parse(String input) {
List cats = new ArrayList();
int i = 0;
while (true) {
i = input.indexOf("name=", i);
if (i < 0) break;
String name = input.substring(i + 6, input.indexOf("\"", i + 7));
i = input.indexOf("age=", i);
int age = Integer.parseInt(input.substring(i + 4, input.indexOf("]", i)));
cats.add(new Cat(name, age));
}
return cats;
}
}
public class Main{
public static void main(String[] args) {
String input = "[Cat[name=\"Minnie\", age=3], Cat[name=\"Pixie\", age=1], Cat[name=\"Kazy\", age=5]]";
List<Cat> cats = Cat.parse(input);
cats.forEach(System.out::println);
cats.stream().filter(e->e.age > 4).forEach(System.out::println);
}
}
Output:
run:
Cat[name="Minnie", age=3]
Cat[name="Pixie", age=1]
Cat[name="Kazy", age=5]
Cat[name="Kazy", age=5]
BUILD SUCCESSFUL (total time: 0 seconds)

Related

How can I use the indexOf() function to find an object with a certain property

I have an object, Pet, and one of the functions is to retrieve its name.
public class pet{
private String petName;
private int petAge;
public pet(String name, int age){
petName = name;
petAge = age;
}
public String getName(){
return petName;
}
public int getAge(){
return petAge;
}
}
I then have an ArrayList which holds a collection of pets as shown in the code below:
import java.util.ArrayList;
pet Dog = new pet("Orio", 2);
pet Cat = new pet("Kathy", 4);
pet Lion = new pet("Usumba", 6);
ArrayList<pet> pets = new ArrayList<>();
pets.add(Dog);
pets.add(Cat);
pets.add(Lion;
I was wondering how I could retrieve the index in the ArrayList or the object that has the name I need. So if I wanted to find out how old Usumba was, how would I do this?
Note: This is not my actual piece of code, it's just used so that I can better explain my problem.
Edit 1
So far, I have the following but I was wondering if there was a better or more efficient way
public int getPetAge(String petName){
int petAge= 0;
for (pet currentPet : pets) {
if (currentPet.getName() == petName){
petAge = currentPet.getAge();
break;
}
}
return petAge;
}
You can't use indexOf() for this purpose, unless you abuse the purpose of the equals() method.
Use a for loop over an int variable that iterates from 0 to the length of the List.
Inside the loop, compare the name if the ith element, and if it's equal to you search term, you've found it.
Something like this:
int index = -1;
for (int i = 0; i < pets.length; i++) {
if (pets.get(i).getName().equals(searchName)) {
index = i;
break;
}
}
// index now holds the found index, or -1 if not found
If you just want to find the object, you don't need the index:
pet found = null;
for (pet p : pets) {
if (p.getName().equals(searchName)) {
found = p;
break;
}
}
// found is now something or null if not found
As the others already stated, you cannot use indexOf() for this directly. It would be possible in certain situations (lambdas, rewriting hashCode/equals etc), but that is usually a bad idea because it would abuse another concept.
Here's a few examples of how we can do that in modern Java:
(as the index topic has already been answered quite well, this only handles direct Object return)
package stackoverflow.filterstuff;
import java.util.ArrayList;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
public class FilterStuff {
public static void main(final String[] args) {
final Pet dog = new Pet("Orio", 2); // again, naming conventions: variable names start with lowercase letters
final Pet cat = new Pet("Kathy", 4);
final Pet lion = new Pet("Usumba", 6);
final ArrayList<Pet> pets = new ArrayList<>();
pets.add(dog);
pets.add(cat);
pets.add(lion);
try {
simpleOldLoop(pets);
} catch (final Exception e) {
e.printStackTrace(System.out);
}
try {
simpleLoopWithLambda(pets);
} catch (final Exception e) {
e.printStackTrace(System.out);
}
try {
filterStreams(pets);
} catch (final Exception e) {
e.printStackTrace(System.out);
}
try {
filterStreamsWithLambda(pets);
} catch (final Exception e) {
e.printStackTrace(System.out);
}
}
private static void simpleOldLoop(final ArrayList<Pet> pPets) {
System.out.println("\nFilterStuff.simpleOldLoop()");
System.out.println("Pet named 'Kathy': " + filterPet_simpleOldLoop(pPets, "Kathy"));
System.out.println("Pet named 'Hans': " + filterPet_simpleOldLoop(pPets, "Hans"));
}
private static Pet filterPet_simpleOldLoop(final ArrayList<Pet> pPets, final String pName) {
if (pPets == null) return null;
for (final Pet pet : pPets) {
if (pet == null) continue;
if (Objects.equals(pet.getName(), pName)) return pet;
}
return null;
}
private static void simpleLoopWithLambda(final ArrayList<Pet> pPets) {
System.out.println("\nFilterStuff.simpleLoopWithLambda()");
System.out.println("Pet named 'Kathy': " + filterPet_simpleLoopWithLambda(pPets, (pet) -> Boolean.valueOf(Objects.equals(pet.getName(), "Kathy"))));
System.out.println("Pet named 'Hans': " + filterPet_simpleLoopWithLambda(pPets, (pet) -> Boolean.valueOf(Objects.equals(pet.getName(), "Hans"))));
}
private static Pet filterPet_simpleLoopWithLambda(final ArrayList<Pet> pPets, final Function<Pet, Boolean> pLambda) {
if (pPets == null) return null;
for (final Pet pet : pPets) {
if (pet == null) continue;
final Boolean result = pLambda.apply(pet);
if (result == Boolean.TRUE) return pet;
}
return null;
}
private static void filterStreams(final ArrayList<Pet> pPets) {
System.out.println("\nFilterStuff.filterStreams()");
System.out.println("Pet named 'Kathy': " + filterPet_filterStreams(pPets, "Kathy"));
System.out.println("Pet named 'Hans': " + filterPet_filterStreams(pPets, "Hans"));
}
private static Pet filterPet_filterStreams(final ArrayList<Pet> pPets, final String pName) {
return pPets.stream().filter(p -> Objects.equals(p.getName(), pName)).findAny().get();
}
private static void filterStreamsWithLambda(final ArrayList<Pet> pPets) {
System.out.println("\nFilterStuff.filterStreamsWithLambda()");
System.out.println("Pet named 'Kathy': " + filterPet_filterStreams(pPets, p -> Objects.equals(p.getName(), "Kathy")));
final Predicate<Pet> pdctHans = p -> Objects.equals(p.getName(), "Hans"); // we can also have 'lambda expressions' stored in variables
System.out.println("Pet named 'Hans': " + filterPet_filterStreams(pPets, pdctHans));
}
private static Pet filterPet_filterStreams(final ArrayList<Pet> pPets, final Predicate<Pet> pLambdaPredicate) {
return pPets.stream().filter(pLambdaPredicate).findAny().get();
}
}
Along with your Pet class, extended by toString():
package stackoverflow.filterstuff;
public class Pet { // please stick to naming conventions: classes start with uppercase letters!
private final String petName;
private final int petAge;
public Pet(final String name, final int age) {
petName = name;
petAge = age;
}
public String getName() {
return petName;
}
public int getAge() {
return petAge;
}
#Override public String toString() {
return "Pet [Name=" + petName + ", Age=" + petAge + "]";
}
}

How can i print a void method that iterates through a LinkedList

I'm creating a class that monitors the price, address and number of bedrooms within a house. I'm trying to call the result for my twoBeds method. Pretty sure my Sysout is completely wrong but I dont know where to start.
public class House {
int price;
int bedrooms;
String address;
public House(int a, int b, String c) {
price = a;
bedrooms = b;
address = c;
}
static List<House> agency = new LinkedList<House>();
public static int noHouse() {
return agency.size();
}
public static void twoBeds() {
for (ListIterator<House> it = agency.listIterator(); it.hasNext(); ) {
House h = it.next();
if (h.bedrooms == 2) {
System.out.println(h.address);
}
}
}
public static void main(String[] args) {
House h1 = new House(12, 2, "address");
agency.add(h1);
System.out.println(twoBeds());
}
}
System.out.println() accepts a parameter, void return type means that there is nothing to return.
You need to return something to print it.
Option 1: Do not pass void function in System.out:
public static void main(String[] args) {
House h1 = new House(12, 2, "address");
agency.add(h1);
twoBeds();
}
Option 2: Change return type of twoBeds():
public static List<String> twoBeds() {
List<String> matchedAddresses = new ArrayList<>();
for (ListIterator<House> it = agency.listIterator(); it.hasNext(); ) {
House h = it.next();
if (h.bedrooms == 2) {
matchedAddresses.add(h.address);
}
}
return matchedAddresses;
}

Refactoring code to be Generics and functional interface

I have two interfaces one can be used to find some statistics by Key and Value and the other is used to visit an object and iterate over it, the first one has the following methods:
public interface Statistic {
public String getKey();
public Object getValue();
public String getDetails();
}
And here is the implementation for it:
public class Collector implements Statistic {
private String key;
private int val;
public Collector(String key, int val) {
this.key = key;
this.val = val;
}
public void setValue(int val) {
this.val = val;
}
#Override
public String getKey() {
return key;
}
#Override
public Integer getValue() {
return val;
}
#Override
public String getDetails() {
return null;
}
}
And the other one has the following:
public interface StatisticsCollector<T extends Object, S extends Statistic> {
public String getName();
public void visit(T object);
public Iterator<S> calculatedStatistics();
}
And here is the implementation for it:
public class CalculateFromObject<K, V> implements StatisticsCollector<Object, Collector> {
EmployeeValidator empValidator = new EmployeeValidator();
StringValidator strValidator = new StringValidator();
#Override
public String getName() {
return null;
}
#Override
public void visit(Object object) {
if (object instanceof String) {
String str = object.toString();
int upperCaseCount = strValidator.upperCaseFreq(str);
strValidator.set.add(new Collector("Upper Case Letters: ", upperCaseCount));
int lowerCaseCount = strValidator.lowerCaseFreq(str);
strValidator.set.add(new Collector("Lower Case Letters: ", lowerCaseCount));
int digitsCount = strValidator.digitFreq(str);
strValidator.set.add(new Collector("Digits Count: ", digitsCount));
int wordCount = strValidator.wordFreq(str);
strValidator.set.add(new Collector("Words Count: ", wordCount));
int nonWordCount = strValidator.nonWordFreq(str);
strValidator.set.add(new Collector("Non Word Count: ", nonWordCount));
} else if (object instanceof Employee) {
Employee emp = (Employee) object;
empValidator.salaryValidator(emp);
empValidator.birthDateValidator(emp);
empValidator.birthPlaceValidator(emp);
empValidator.resignationDateValidator(emp);
empValidator.positionValidator(emp);
}
}
#Override
public Iterator<Collector> calculatedStatistics() {
return empValidator.set.iterator();
}
}
And in my package I have a bean for Employee which has few properties like firstName, lastName, salary and position with their setters and getters.
I would like to do some validations like get me the number of employees which has a salary of x and been born in 1990 and did the following class for these validations:
public class EmployeeValidator {
public Set<Collector> set = new HashSet<>();
public void salaryValidator(Employee emp) {
int count = 0;
// each collector consist of a condition (function), key, value (always incremented)
if (emp.getSalary() < 350) {
set.add(new Collector("Employee with salaries less than 350JD: ", ++count));
} else if (emp.getSalary() >= 350 && emp.getSalary() < 600) {
set.add(new Collector("Employee with salaries between 350JD And 600JD: ", ++count));
} else if (emp.getSalary() >= 600 && emp.getSalary() < 1200) {
set.add(new Collector("Employee with salaries between 600JD And 1200JD ", ++count));
} else if (emp.getSalary() >= 1200) {
set.add(new Collector("Employee with salaries more than 1200JD: ", ++count));
}
}
public void birthDateValidator(Employee emp) {
for (Collector stats : set) {
if (("Employees that where born in " + emp.getBirthDate().getYear() + " = ").equals(stats.getKey())) {
count(stats);
return;
}
}
set.add(new Collector("Employees that where born in " + emp.getBirthDate().getYear() + " = ", 1));
}
public void birthPlaceValidator(Employee emp) {
for (Collector stats : set) {
if (("Employees that where born in " + emp.getBirthPlace() + " = ").equals(stats.getKey())) {
count(stats);
return;
}
}
set.add(new Collector("Employees that where born in " + emp.getBirthPlace() + " = ", 1));
}
public void resignationDateValidator(Employee emp) {
for (Collector stats : set) {
if (("Employees that where Resignation in " + emp.getResignationDate().getYear() + " = ").equals(
stats.getKey())) {
count(stats);
return;
}
}
set.add(new Collector("Employees that where Resignation in " + emp.getResignationDate().getYear() + " = ", 1));
}
public void positionValidator(Employee emp) {
for (Collector stats : set) {
if (("Employees that occupy the " + emp.getPosition() + " position = ").equals(stats.getKey())) {
count(stats);
return;
}
}
set.add(new Collector("Employees that occupy the " + emp.getPosition() + " position = ", 1));
}
private void count(Collector stats) {
int counter = stats.getValue() + 1;
stats.setValue(counter);
}
}
And I have another class to validate strings and see how many uppercase letters a string has, how many lower case it has...etc
As you can see in the visit method in the CalculateFromObject class im calling all my methods to do the validations, everything is working fine and I am getting the expected results, but my code is not very efficient as I would like to make it generic and make it accept any type of object, I have done few tries but im stuck.
I have tried to write a functional interface called Conditions that has one method that I can pass a condition and check it like the following:
public interface Conditions {
boolean checkCondition(Object obj);
}
So can someone suggest what is the best way to change my code to be generic and accept any type of objects like Student for example and to be as clean as possible maybe by applying a design pattern?
There's a lot of overhead in your classes and misunderstanding of interfaces against POJO's (simple classes). On a high level you should do the following:
1). Remove interface Statistic and class Collectors. They are just incapsulating data. Instead - create a POJO Employee with necessary fields + getters + setters. Don't use 'key-value`, give those fields meaningful names:
public class Employee
{
private String name;
private int id;
private double salary;
...
public String getName() {...}
public void setName(..) {...}
// other getters / setters
}
Create constructor if needed
2) Looks like your Employee class is also redundant, remove it. Use new Employee instead
3) Use Collections framework to store collections of your employee's instances.
`List<Employee> employees = new ArrayList<>();
employees.add(new Employee(.... )); `
4). Create interface EmployeeValidator with validating methods and implement it:
public interface EmployeeValidator {
void validate(List<Employee> employees);
}
5) If you want to operate some statistics data, create a separate Statistics class which will operate on collections of employees, e.g.
public class Statistics {
public double getAvgSalary(List<Employee> employees)
{
double avgSalary = 0;
for (Employee e : employees) {
....
}
}
}

Stable Marriage algorithm java code

I need to implement makeMatches method in stable marriage class.
Code needs to follow this algorithm:
set each person to be free;
while (some man m with a nonempty preference list is free) {
w = first woman on m's list;
if (some man p is engaged to w) {
set p to be free
}
set m and w to be engaged to each other
for (each successor q of m on w's list) {
delete w from q's preference list
delete q from w's preference list
}
}
However I can not find my error. The program makes couples depend on their first choices but after that doesn't continue and set couples.
Could you look at my code and tell me what is wrong with it?
Person.java is provided from instructor:
import java.util.*;
public class Person {
public static final int NOBODY = -1;
private String name;
private List<Integer> preferences;
private List<Integer> oldPreferences;
private int partner;
public Person(String name) {
this.name = name;
preferences = new ArrayList<Integer>();
oldPreferences = new ArrayList<Integer>();
erasePartner();
}
public void erasePartner() {
partner = NOBODY;
}
public boolean hasPartner() {
return partner != NOBODY;
}
public int getPartner() {
return partner;
}
public void setPartner(int partner) {
this.partner = partner;
}
public String getName() {
return name;
}
public boolean hasChoices() {
return !preferences.isEmpty();
}
public int getFirstChoice() {
return preferences.get(0);
}
public void addChoice(int person) {
preferences.add(person);
oldPreferences.add(person);
}
public List<Integer> getChoices() {
return preferences;
}
public int getPartnerRank() {
return oldPreferences.indexOf(partner) + 1;
}
}
And StablaMarriage.java is the one I need to implement code in.
import java.io.*;
import java.util.*;
public class StableMarriage {
public static final String LIST_END = "END";
public static void main(String[] args) throws FileNotFoundException {
Scanner console = new Scanner(System.in);
System.out.print("What is the input file? ");
String fileName = console.nextLine();
Scanner input = new Scanner(new File(fileName));
System.out.println();
List<Person> men = readHalf(input);
List<Person> women = readHalf(input);
makeMatches(men, women);
writeList(men, women, "Matches for men");
writeList(women, men, "Matches for women");
}
public static Person readPerson(String line) {
int index = line.indexOf(":");
Person result = new Person(line.substring(0, index));
Scanner data = new Scanner(line.substring(index + 1));
while (data.hasNextInt()) {
result.addChoice(data.nextInt());
}
return result;
}
public static List<Person> readHalf(Scanner input) {
List<Person> result = new ArrayList<Person>();
String line = input.nextLine();
while (!line.equals(LIST_END)) {
result.add(readPerson(line));
line = input.nextLine();
}
return result;
}
public static void makeMatches(List<Person> list1, List<Person> list2) {
for (Person eachMan : list1){
eachMan.setPartner(-1);
}
for (Person eachWoman : list2){
eachWoman.setPartner(-1);
}
for (Person man : list1){
for (Person woman : list2){
while (man.hasChoices() && !man.hasPartner()){
int choosenWoman = man.getFirstChoice();
for (Person p : list1){
if (p.getPartner()== choosenWoman){
p.setPartner(-1);
}
}
man.setPartner(choosenWoman);
list2.get(choosenWoman).setPartner(list1.indexOf(man));
List manList= man.getChoices();
List womanList = woman.getChoices();
for (int q= womanList.size()-1; q>=0; q--){
if(q>womanList.indexOf(list1.indexOf(man)))
manList.remove(manList.indexOf(man.getFirstChoice()));
womanList.remove(q);
}
}
}
}
}
public static void writeList(List<Person> list1, List<Person> list2,
String title) {
System.out.println(title);
System.out.println("Name Choice Partner");
System.out.println("--------------------------------------");
int sum = 0;
int count = 0;
for (Person p : list1) {
System.out.printf("%-15s", p.getName());
if (!p.hasPartner()) {
System.out.println(" -- nobody");
} else {
int rank = p.getPartnerRank();
sum += rank;
count++;
System.out.printf("%4d %s\n", rank,
list2.get(p.getPartner()).getName());
}
}
System.out.println("Mean choice = " + (double) sum / count);
System.out.println();
}
}

How to use Comparator in Java to sort

I learned how to use the comparable but I'm having difficulty with the Comparator. I am having a error in my code:
Exception in thread "main" java.lang.ClassCastException: New.People cannot be cast to java.lang.Comparable
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at New.TestPeople.main(TestPeople.java:18)
Here is my code:
import java.util.Comparator;
public class People implements Comparator {
private int id;
private String info;
private double price;
public People(int newid, String newinfo, double newprice) {
setid(newid);
setinfo(newinfo);
setprice(newprice);
}
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public String getinfo() {
return info;
}
public void setinfo(String info) {
this.info = info;
}
public double getprice() {
return price;
}
public void setprice(double price) {
this.price = price;
}
public int compare(Object obj1, Object obj2) {
Integer p1 = ((People) obj1).getid();
Integer p2 = ((People) obj2).getid();
if (p1 > p2) {
return 1;
} else if (p1 < p2){
return -1;
} else {
return 0;
}
}
}
import java.util.ArrayList;
import java.util.Collections;
public class TestPeople {
public static void main(String[] args) {
ArrayList peps = new ArrayList();
peps.add(new People(123, "M", 14.25));
peps.add(new People(234, "M", 6.21));
peps.add(new People(362, "F", 9.23));
peps.add(new People(111, "M", 65.99));
peps.add(new People(535, "F", 9.23));
Collections.sort(peps);
for (int i = 0; i < peps.size(); i++){
System.out.println(peps.get(i));
}
}
}
I believe it has to do something with the casting in the compare method but I was playing around with it and still could not find the solution
There are a couple of awkward things with your example class:
it's called People while it has a price and info (more something for objects, not people);
when naming a class as a plural of something, it suggests it is an abstraction of more than one thing.
Anyway, here's a demo of how to use a Comparator<T>:
public class ComparatorDemo {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Joe", 24),
new Person("Pete", 18),
new Person("Chris", 21)
);
Collections.sort(people, new LexicographicComparator());
System.out.println(people);
Collections.sort(people, new AgeComparator());
System.out.println(people);
}
}
class LexicographicComparator implements Comparator<Person> {
#Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
class AgeComparator implements Comparator<Person> {
#Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
}
class Person {
String name;
int age;
Person(String n, int a) {
name = n;
age = a;
}
#Override
public String toString() {
return String.format("{name=%s, age=%d}", name, age);
}
}
EDIT
And an equivalent Java 8 demo would look like this:
public class ComparatorDemo {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Joe", 24),
new Person("Pete", 18),
new Person("Chris", 21)
);
Collections.sort(people, (a, b) -> a.name.compareToIgnoreCase(b.name));
System.out.println(people);
Collections.sort(people, (a, b) -> a.age < b.age ? -1 : a.age == b.age ? 0 : 1);
System.out.println(people);
}
}
Here's a super short template to do the sorting right away :
Collections.sort(people, new Comparator<Person>() {
#Override
public int compare(final Person lhs, Person rhs) {
// TODO return 1 if rhs should be before lhs
// return -1 if lhs should be before rhs
// return 0 otherwise (meaning the order stays the same)
}
});
If it's hard to remember, try to just remember that it's similar (in terms of the sign of the number) to:
lhs-rhs
That's in case you want to sort in ascending order : from smallest number to largest number.
Use People implements Comparable<People> instead; this defines the natural ordering for People.
A Comparator<People> can also be defined in addition, but People implements Comparator<People> is not the right way of doing things.
The two overloads for Collections.sort are different:
<T extends Comparable<? super T>> void sort(List<T> list)
Sorts Comparable objects using their natural ordering
<T> void sort(List<T> list, Comparator<? super T> c)
Sorts whatever using a compatible Comparator
You're confusing the two by trying to sort a Comparator (which is again why it doesn't make sense that Person implements Comparator<Person>). Again, to use Collections.sort, you need one of these to be true:
The type must be Comparable (use the 1-arg sort)
A Comparator for the type must be provided (use the 2-args sort)
Related questions
When to use Comparable vs Comparator
Sorting an ArrayList of Contacts
Also, do not use raw types in new code. Raw types are unsafe, and it's provided only for compatibility.
That is, instead of this:
ArrayList peps = new ArrayList(); // BAD!!! No generic safety!
you should've used the typesafe generic declaration like this:
List<People> peps = new ArrayList<People>(); // GOOD!!!
You will then find that your code doesn't even compile!! That would be a good thing, because there IS something wrong with the code (Person does not implements Comparable<Person>), but because you used raw type, the compiler didn't check for this, and instead you get a ClassCastException at run-time!!!
This should convince you to always use typesafe generic types in new code. Always.
See also
What is a raw type and why shouldn't we use it?
For the sake of completeness, here's a simple one-liner compare method:
Collections.sort(people, new Comparator<Person>() {
#Override
public int compare(Person lhs, Person rhs) {
return Integer.signum(lhs.getId() - rhs.getId());
}
});
Java 8 added a new way of making Comparators that reduces the amount of code you have to write, Comparator.comparing. Also check out Comparator.reversed
Here's a sample
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import static org.junit.Assert.assertTrue;
public class ComparatorTest {
#Test
public void test() {
List<Person> peopleList = new ArrayList<>();
peopleList.add(new Person("A", 1000));
peopleList.add(new Person("B", 1));
peopleList.add(new Person("C", 50));
peopleList.add(new Person("Z", 500));
//sort by name, ascending
peopleList.sort(Comparator.comparing(Person::getName));
assertTrue(peopleList.get(0).getName().equals("A"));
assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("Z"));
//sort by name, descending
peopleList.sort(Comparator.comparing(Person::getName).reversed());
assertTrue(peopleList.get(0).getName().equals("Z"));
assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("A"));
//sort by age, ascending
peopleList.sort(Comparator.comparing(Person::getAge));
assertTrue(peopleList.get(0).getAge() == 1);
assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1000);
//sort by age, descending
peopleList.sort(Comparator.comparing(Person::getAge).reversed());
assertTrue(peopleList.get(0).getAge() == 1000);
assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1);
}
class Person {
String name;
int age;
Person(String n, int a) {
name = n;
age = a;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
}
For the sake of completeness.
Using Java8
people.sort(Comparator.comparingInt(People::getId));
if you want in descending order
people.sort(Comparator.comparingInt(People::getId).reversed());
You want to implement Comparable, not Comparator. You need to implement the compareTo method. You're close though. Comparator is a "3rd party" comparison routine. Comparable is that this object can be compared with another.
public int compareTo(Object obj1) {
People that = (People)obj1;
Integer p1 = this.getId();
Integer p2 = that.getid();
if (p1 > p2 ){
return 1;
}
else if (p1 < p2){
return -1;
}
else
return 0;
}
Note, you may want to check for nulls in here for getId..just in case.
Two corrections:
You have to make an ArrayList of People objects:
ArrayList<People> preps = new ArrayList<People>();
After adding the objects to the preps, use:
Collections.sort(preps, new CompareId());
Also, add a CompareId class as:
class CompareId implements Comparator {
public int compare(Object obj1, Object obj2) {
People t1 = (People)obj1;
People t2 = (People)obj2;
if (t1.marks > t2.marks)
return 1;
else
return -1;
}
}
Here's an example of a Comparator that will work for any zero arg method that returns a Comparable. Does something like this exist in a jdk or library?
import java.lang.reflect.Method;
import java.util.Comparator;
public class NamedMethodComparator implements Comparator<Object> {
//
// instance variables
//
private String methodName;
private boolean isAsc;
//
// constructor
//
public NamedMethodComparator(String methodName, boolean isAsc) {
this.methodName = methodName;
this.isAsc = isAsc;
}
/**
* Method to compare two objects using the method named in the constructor.
*/
#Override
public int compare(Object obj1, Object obj2) {
Comparable comp1 = getValue(obj1, methodName);
Comparable comp2 = getValue(obj2, methodName);
if (isAsc) {
return comp1.compareTo(comp2);
} else {
return comp2.compareTo(comp1);
}
}
//
// implementation
//
private Comparable getValue(Object obj, String methodName) {
Method method = getMethod(obj, methodName);
Comparable comp = getValue(obj, method);
return comp;
}
private Method getMethod(Object obj, String methodName) {
try {
Class[] signature = {};
Method method = obj.getClass().getMethod(methodName, signature);
return method;
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
private Comparable getValue(Object obj, Method method) {
Object[] args = {};
try {
Object rtn = method.invoke(obj, args);
Comparable comp = (Comparable) rtn;
return comp;
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
}
public static Comparator<JobSet> JobEndTimeComparator = new Comparator<JobSet>() {
public int compare(JobSet j1, JobSet j2) {
int cost1 = j1.cost;
int cost2 = j2.cost;
return cost1-cost2;
}
};
The solution can be optimized in following way:
Firstly, use a private inner class as the scope for the fields is to be the enclosing class TestPeople so as the implementation of class People won't get exposed to outer world. This can be understood in terms of creating an APIthat expects a sorted list of people
Secondly, using the Lamba expression(java 8) which reduces the code, hence development effort
Hence code would be as below:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class TestPeople {
public static void main(String[] args) {
ArrayList<People> peps = new ArrayList<>();// Be specific, to avoid
// classCast Exception
TestPeople test = new TestPeople();
peps.add(test.new People(123, "M", 14.25));
peps.add(test.new People(234, "M", 6.21));
peps.add(test.new People(362, "F", 9.23));
peps.add(test.new People(111, "M", 65.99));
peps.add(test.new People(535, "F", 9.23));
/*
* Collections.sort(peps);
*
* for (int i = 0; i < peps.size(); i++){
* System.out.println(peps.get(i)); }
*/
// The above code can be replaced by followin:
peps.sort((People p1, People p2) -> p1.getid() - p2.getid());
peps.forEach((p) -> System.out.println(" " + p.toString()));
}
private class People {
private int id;
#Override
public String toString() {
return "People [id=" + id + ", info=" + info + ", price=" + price + "]";
}
private String info;
private double price;
public People(int newid, String newinfo, double newprice) {
setid(newid);
setinfo(newinfo);
setprice(newprice);
}
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public String getinfo() {
return info;
}
public void setinfo(String info) {
this.info = info;
}
public double getprice() {
return price;
}
public void setprice(double price) {
this.price = price;
}
}
}
Here is a lambda version of comparator. This will sort a string list according to length.
Collections.sort(str, (str1, str2) -> {
if(str1.length() < str2.length())
return 1;
else if(str2.length() < str1.length())
return -1;
else
return 0;
});
You should use the overloaded sort(peps, new People()) method
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test
{
public static void main(String[] args)
{
List<People> peps = new ArrayList<>();
peps.add(new People(123, "M", 14.25));
peps.add(new People(234, "M", 6.21));
peps.add(new People(362, "F", 9.23));
peps.add(new People(111, "M", 65.99));
peps.add(new People(535, "F", 9.23));
Collections.sort(peps, new People().new ComparatorId());
for (int i = 0; i < peps.size(); i++)
{
System.out.println(peps.get(i));
}
}
}
class People
{
private int id;
private String info;
private double price;
public People()
{
}
public People(int newid, String newinfo, double newprice) {
setid(newid);
setinfo(newinfo);
setprice(newprice);
}
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public String getinfo() {
return info;
}
public void setinfo(String info) {
this.info = info;
}
public double getprice() {
return price;
}
public void setprice(double price) {
this.price = price;
}
class ComparatorId implements Comparator<People>
{
#Override
public int compare(People obj1, People obj2) {
Integer p1 = obj1.getid();
Integer p2 = obj2.getid();
if (p1 > p2) {
return 1;
} else if (p1 < p2){
return -1;
} else {
return 0;
}
}
}
}
Here is my answer for a simple comparator tool
public class Comparator {
public boolean isComparatorRunning = false;
public void compareTableColumns(List<String> tableNames) {
if(!isComparatorRunning) {
isComparatorRunning = true;
try {
for (String schTableName : tableNames) {
Map<String, String> schemaTableMap = ComparatorUtil.getSchemaTableMap(schTableName);
Map<String, ColumnInfo> primaryColMap = ComparatorUtil.getColumnMetadataMap(DbConnectionRepository.getConnectionOne(), schemaTableMap);
Map<String, ColumnInfo> secondaryColMap = ComparatorUtil.getColumnMetadataMap(DbConnectionRepository.getConnectionTwo(), schemaTableMap);
ComparatorUtil.publishColumnInfoOutput("Comparing table : "+ schemaTableMap.get(CompConstants.TABLE_NAME));
compareColumns(primaryColMap, secondaryColMap);
}
} catch (Exception e) {
ComparatorUtil.publishColumnInfoOutput("ERROR"+e.getMessage());
}
isComparatorRunning = false;
}
}
public void compareColumns(Map<String, ColumnInfo> primaryColMap, Map<String, ColumnInfo> secondaryColMap) {
try {
boolean isEqual = true;
for(Map.Entry<String, ColumnInfo> entry : primaryColMap.entrySet()) {
String columnName = entry.getKey();
ColumnInfo primaryColInfo = entry.getValue();
ColumnInfo secondaryColInfo = secondaryColMap.remove(columnName);
if(secondaryColInfo == null) {
// column is not present in Secondary Environment
ComparatorUtil.publishColumnInfoOutput("ALTER", primaryColInfo);
isEqual = false;
continue;
}
if(!primaryColInfo.equals(secondaryColInfo)) {
isEqual = false;
// Column not equal in secondary env
ComparatorUtil.publishColumnInfoOutput("MODIFY", primaryColInfo);
}
}
if(!secondaryColMap.isEmpty()) {
isEqual = false;
for(Map.Entry<String, ColumnInfo> entry : secondaryColMap.entrySet()) {
// column is not present in Primary Environment
ComparatorUtil.publishColumnInfoOutput("DROP", entry.getValue());
}
}
if(isEqual) {
ComparatorUtil.publishColumnInfoOutput("--Exact Match");
}
} catch (Exception e) {
ComparatorUtil.publishColumnInfoOutput("ERROR"+e.getMessage());
}
}
public void compareTableColumnsValues(String primaryTableName, String primaryColumnNames, String primaryCondition, String primaryKeyColumn,
String secTableName, String secColumnNames, String secCondition, String secKeyColumn) {
if(!isComparatorRunning) {
isComparatorRunning = true;
Connection conn1 = DbConnectionRepository.getConnectionOne();
Connection conn2 = DbConnectionRepository.getConnectionTwo();
String query1 = buildQuery(primaryTableName, primaryColumnNames, primaryCondition, primaryKeyColumn);
String query2 = buildQuery(secTableName, secColumnNames, secCondition, secKeyColumn);
try {
Map<String,Map<String, Object>> query1Data = executeAndRefactorData(conn1, query1, primaryKeyColumn);
Map<String,Map<String, Object>> query2Data = executeAndRefactorData(conn2, query2, secKeyColumn);
for(Map.Entry<String,Map<String, Object>> entry : query1Data.entrySet()) {
String key = entry.getKey();
Map<String, Object> value = entry.getValue();
Map<String, Object> secondaryValue = query2Data.remove(key);
if(secondaryValue == null) {
ComparatorUtil.publishColumnValuesInfoOutput("NO SUCH VALUE AVAILABLE IN SECONDARY DB "+ value.toString());
continue;
}
compareMap(value, secondaryValue, key);
}
if(!query2Data.isEmpty()) {
ComparatorUtil.publishColumnValuesInfoOutput("Extra Values in Secondary table "+ ((Map)query2Data.values()).values().toString());
}
} catch (Exception e) {
ComparatorUtil.publishColumnValuesInfoOutput("ERROR"+e.getMessage());
}
isComparatorRunning = false;
}
}
private void compareMap(Map<String, Object> primaryValues, Map<String, Object> secondaryValues, String columnIdentification) {
for(Map.Entry<String, Object> entry : primaryValues.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Object secValue = secondaryValues.get(key);
if(value!=null && secValue!=null && !String.valueOf(value).equalsIgnoreCase(String.valueOf(secValue))) {
ComparatorUtil.publishColumnValuesInfoOutput(columnIdentification+" : Secondary Table does not match value ("+ value +") for column ("+ key+")");
}
if(value==null && secValue!=null) {
ComparatorUtil.publishColumnValuesInfoOutput(columnIdentification+" : Values not available in primary table for column "+ key);
}
if(value!=null && secValue==null) {
ComparatorUtil.publishColumnValuesInfoOutput(columnIdentification+" : Values not available in Secondary table for column "+ key);
}
}
}
private String buildQuery(String tableName, String column, String condition, String keyCol) {
if(!"*".equalsIgnoreCase(column)) {
String[] keyColArr = keyCol.split(",");
for(String key: keyColArr) {
if(!column.contains(key.trim())) {
column+=","+key.trim();
}
}
}
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("select "+column+" from "+ tableName);
if(!ComparatorUtil.isNullorEmpty(condition)) {
queryBuilder.append(" where 1=1 and "+condition);
}
return queryBuilder.toString();
}
private Map<String,Map<String, Object>> executeAndRefactorData(Connection connection, String query, String keyColumn) {
Map<String,Map<String, Object>> result = new HashMap<String, Map<String,Object>>();
try {
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
resultSet.setFetchSize(1000);
if (resultSet != null && !resultSet.isClosed()) {
while (resultSet.next()) {
Map<String, Object> columnValueDetails = new HashMap<String, Object>();
int columnCount = resultSet.getMetaData().getColumnCount();
for (int i=1; i<=columnCount; i++) {
String columnName = String.valueOf(resultSet.getMetaData().getColumnName(i));
Object columnValue = resultSet.getObject(columnName);
columnValueDetails.put(columnName, columnValue);
}
String[] keys = keyColumn.split(",");
String newKey = "";
for(int j=0; j<keys.length; j++) {
newKey += String.valueOf(columnValueDetails.get(keys[j]));
}
result.put(newKey , columnValueDetails);
}
}
} catch (SQLException e) {
ComparatorUtil.publishColumnValuesInfoOutput("ERROR"+e.getMessage());
}
return result;
}
}
Utility Tool for the same
public class ComparatorUtil {
public static Map<String, String> getSchemaTableMap(String tableNameWithSchema) {
if(isNullorEmpty(tableNameWithSchema)) {
return null;
}
Map<String, String> result = new LinkedHashMap<>();
int index = tableNameWithSchema.indexOf(".");
String schemaName = tableNameWithSchema.substring(0, index);
String tableName = tableNameWithSchema.substring(index+1);
result.put(CompConstants.SCHEMA_NAME, schemaName);
result.put(CompConstants.TABLE_NAME, tableName);
return result;
}
public static Map<String, ColumnInfo> getColumnMetadataMap(Connection conn, Map<String, String> schemaTableMap) {
try {
String schemaName = schemaTableMap.get(CompConstants.SCHEMA_NAME);
String tableName = schemaTableMap.get(CompConstants.TABLE_NAME);
ResultSet resultSetConnOne = conn.getMetaData().getColumns(null, schemaName, tableName, null);
Map<String, ColumnInfo> resultSetTwoColInfo = getColumnInfo(schemaName, tableName, resultSetConnOne);
return resultSetTwoColInfo;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/* Number Type mapping
* 12-----VARCHAR
* 3-----DECIMAL
* 93-----TIMESTAMP
* 1111-----OTHER
*/
public static Map<String, ColumnInfo> getColumnInfo(String schemaName, String tableName, ResultSet columns) {
try {
Map<String, ColumnInfo> tableColumnInfo = new LinkedHashMap<String, ColumnInfo>();
while (columns.next()) {
ColumnInfo columnInfo = new ColumnInfo();
columnInfo.setSchemaName(schemaName);
columnInfo.setTableName(tableName);
columnInfo.setColumnName(columns.getString("COLUMN_NAME"));
columnInfo.setDatatype(columns.getString("DATA_TYPE"));
columnInfo.setColumnsize(columns.getString("COLUMN_SIZE"));
columnInfo.setDecimaldigits(columns.getString("DECIMAL_DIGITS"));
columnInfo.setIsNullable(columns.getString("IS_NULLABLE"));
tableColumnInfo.put(columnInfo.getColumnName(), columnInfo);
}
return tableColumnInfo;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static boolean isNullOrEmpty(Object obj) {
if (obj == null)
return true;
if (String.valueOf(obj).equalsIgnoreCase("NULL"))
return true;
if (obj.toString().trim().length() == 0)
return true;
return false;
}
public static boolean isNullorEmpty(String str) {
if(str == null)
return true;
if(str.trim().length() == 0)
return true;
return false;
}
public static void publishColumnInfoOutput(String type, ColumnInfo columnInfo) {
String str = "ALTER TABLE "+columnInfo.getSchemaName()+"."+columnInfo.getTableName();
switch(type.toUpperCase()) {
case "ALTER":
if("NUMBER".equalsIgnoreCase(columnInfo.getDatatype()) || "DATE".equalsIgnoreCase(columnInfo.getDatatype())) {
str += " ADD ("+columnInfo.getColumnName()+" "+ columnInfo.getDatatype()+");";
} else {
str += " ADD ("+columnInfo.getColumnName()+" "+ columnInfo.getDatatype() +"("+columnInfo.getColumnsize()+"));";
}
break;
case "DROP":
str += " DROP ("+columnInfo.getColumnName()+");";
break;
case "MODIFY":
if("NUMBER".equalsIgnoreCase(columnInfo.getDatatype()) || "DATE".equalsIgnoreCase(columnInfo.getDatatype())) {
str += " MODIFY ("+columnInfo.getColumnName()+" "+ columnInfo.getDatatype()+");";
} else {
str += " MODIFY ("+columnInfo.getColumnName()+" "+ columnInfo.getDatatype() +"("+columnInfo.getColumnsize()+"));";
}
break;
}
publishColumnInfoOutput(str);
}
public static Map<Integer, String> allJdbcTypeName = null;
public static Map<Integer, String> getAllJdbcTypeNames() {
Map<Integer, String> result = new HashMap<Integer, String>();
if(allJdbcTypeName != null)
return allJdbcTypeName;
try {
for (Field field : java.sql.Types.class.getFields()) {
result.put((Integer) field.get(null), field.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
return allJdbcTypeName=result;
}
public static String getStringPlaces(String[] attribs) {
String params = "";
for(int i=0; i<attribs.length; i++) { params += "?,"; }
return params.substring(0, params.length()-1);
}
}
Column Info Class
public class ColumnInfo {
private String schemaName;
private String tableName;
private String columnName;
private String datatype;
private String columnsize;
private String decimaldigits;
private String isNullable;
If you are using Java 8 then it's better to use below code like this:
Comparator<People> comparator = Comparator.comparing(People::getName);
And then simply use:
Collections.sort(list, comparator);
If you are using Java 7 or below then you can use a comparator for customized sorting order by implementing compare method.
For example:
import java.util.Comparator;
public class PeopleNameComparator implements Comparator<People> {
#Override
public int compare(People people1, People people2) {
return people1.getName().compareTo(people2.getName());
}
}
And then simply use like this:
Collections.sort(list, new PeopleNameComparator);
Do not waste time implementing Sorting Algorithm by your own. Instead use Collections.sort() to sort data.

Categories

Resources