How to implement a Chain Comparator - java

I am creating the program that sorts an ArrayList of objects with parameters:
String name
int types(1-3)
int diffs(stands for difficulty, 1-3))
and in days(days until deadline).
I want to sort the list of objects using all parameters at sequentially in that order. I'm just getting used to comparators and was wondering how exactly I would implement a comparator chain in my code.
I know there are other methods of doing this such as using if else statements inside one comparator or using compareToBuilder but I'm not sure which is best yet or if there are any other alternative methods I should consider.
Code for main:
import java.io.*;
import java.util.*;
public class InputItem
{
public int row;
public static void main(String args[])
{
String again;
String names[] = new String[100];
int types[] = new int[100];
int diffs[] = new int[100];
int days[] = new int[100];
int row=0;
do{
System.out.println("Please input assignment name:");
Scanner newNames = new Scanner(System.in);
String name = newNames.nextLine();
names[row] =name;
System.out.println("Please input assignment type:");
Scanner typeI = new Scanner(System.in);
int type = typeI.nextInt();
types[row] = type;
System.out.println("Please input assignment difficulty:");
Scanner diffI = new Scanner(System.in);
int diff = diffI.nextInt();
diffs[row] = diff;
// input days...
System.out.println("Would you like to add another item? Enter 'Yes' or 'No'");
Scanner input = new Scanner(System.in);
again = input.next();
row++;
}
while(again.equalsIgnoreCase("Yes"));
List<Itemss> WORK = new ArrayList<Itemss>();
for(int count = 0; count<row; count++)
{
WORK.add(new Itemss(((types[count])), (names[count])));
}
Collections.sort(WORK, new COMP());
System.out.println("Sorted List Entries: ");
for(Itemss a: WORK)
{
System.out.println(a);
}
}
}
Code for Itemss class and comparator
import java.util.*;
class COMP implements Comparator<Itemss>
{
#Override //overides compareTo() method
public int compare(Itemss a1, Itemss a2)
{
if((a1).getType()< (a2).getType())
{
return 1;
}
else
{
return -1;
}
}
}
public class Itemss
{
private String name;
private int type;
//private int diff;
//private int days;
public Itemss(int t, String n)
{
name = n;
type = t;
//diff = df;
//days = da;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getType()
{
return type;
}
public void setType(int type)
{
this.type = type;
}
public String toString()
{
return this.name + "-->Type:" + this.type ;
}
}

To follow is a basic approach. Your previous Comparator was strictly comparing greater than or less than. To chain, compare the next set of variables when current variables are equal. Here is an example:
class COMP implements Comparator<Items> {
#Override // overides compareTo() method
public int compare(Items a1, Items a2) {
if (a1.getType() < a2.getType()) {
return 1;
} else if (a1.getType() > a2.getType()) {
return -1;
} else if (a1.getDiff() < a2.getDiff()) {
return 1;
} else if (a1.getDiff() > a2.getDiff()) {
return -1;
} else if (a1.getDays() < a2.getDays()) {
return 1;
} else if (a1.getDays() > a2.getDays()) {
return -1;
}
return 0;
}
}
Which creates a sample output like:
-- AFTER SORT --
Items [name=Item 8, type=3, diff=3, days=5]
Items [name=Item 9, type=3, diff=2, days=4]
Items [name=Item 7, type=3, diff=1, days=3]
Items [name=Item 4, type=2, diff=3, days=10]
Items [name=Item 5, type=2, diff=2, days=6]
Items [name=Item 6, type=2, diff=1, days=12]
Items [name=Item 3, type=1, diff=2, days=11]
Items [name=Item 1, type=1, diff=2, days=10]
Items [name=Item 2, type=1, diff=1, days=9]

You should use something like this:
public void order(List<MyObject> myList) {
Comparator<MyObject> byName = new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
if (o1.getName() != null && o2.getName() != null) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
return -1;
}
};
Comparator<MyObject> byType = new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
if (o1.getType() != null && o2.getType() != null) {
return o1.getType().compareTo(o2.getType());
}
return -1;
}
};
Comparator<MyObject> byDiffs = new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
if (o1.getDiffs() != null && o2.getDiffs() != null) {
return o1.getDiffs().compareTo(o2.getDiffs());
}
return -1;
}
};
Comparator<MyObject> byDays = new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
if (o1.getDays() != null && o2.getDays() != null) {
return o1.getDays().compareTo(o2.getDays());
}
return -1;
}
};
ComparatorChain chain = new ComparatorChain();
chain.addComparator(byName);
chain.addComparator(byType);
chain.addComparator(byDiffs);
chain.addComparator(byDays);
Collections.sort(myList, chain);
}

Related

How to Implement Binary Search Manually In My Own ADT [ JAVA ]

Alright I am stuck on how do I implement this binary search that will receive data from other classes.
I am trying to implement it in my own ADT.
I have implemented a List ADT manually but now I want to add in a search operation which utilizes binary search algorithm manually and doesn't uses any built in Java API.
Example this is my sorted list interface that I implemented manually.
public class SortedArrayList<T extends Comparable<T>> implements SortedListInterface<T>{
private boolean binarySearch(// What parameters should I receive from Student Object?) {
// This will be my binary search implementation
}
}
The problem is I will be creating a Student class where I will add the instances of the student class into the sortedArrayList above.
Like how am I going to receive the data to be put into the binary search algorithm in a generics typed sortedArrayList?
Do note I am not allowed to use any JAVA Built-IN API , everything must be implemented manually else I can finish this easily but its a pain now since its limited.
Example I want to binary search by Student name from Student's class. How will I need to implement and receive data into this manually implemented ADT of mine?
public class SortedArrayList<T extends Comparable<T>> implements SortedListInterface<T>{
private T[] list;
private boolean binarySearch(int first, int last, T desiredItem) {
int mid = (first + last) / 2;
if(desiredItem.getFullName().equals(list[mid])
// This part over here. How do I access attributes from Student class in this ADT so that I can access the data and do comparison for the binary search..
}
}
How do I access attributes from Student class into my own ADT so that I can do comparisons on binary search algorithm?!
I am literally stuck.
I would appreciate someone giving me directions.
I repeat again no BUILT-IN APIs from JAVA, implementation manually only
ADT SortedList Interface
public interface SortedListInterface <T extends Comparable<T>> {
public boolean add(T element);
public T get(int index);
public boolean search(T element);
public T remove(int index);
public void clear();
public int getLength();
public boolean isEmpty();
public boolean isFull();
}
ADT SortedList Implementation Code
public class SortedArrayList<T extends Comparable<T>> implements SortedListInterface<T>{
//Data Types
private T[] list;
private int length;
private static final int SIZE = 10;
// Constructors
public SortedArrayList() {
this(SIZE);
}
public SortedArrayList(int size) {
length = 0;
list = (T[]) new Comparable[SIZE]; // an array of instances of a class implementing Comparable interface and able to use compareto method but its overidden instead
}
// Setter & Getters
#Override
public int getLength() {
return length;
}
#Override
public boolean isEmpty() {
return length == 0;
}
#Override
public boolean isFull() {
return false;
}
#Override
public void clear() {
length = 0;
}
// Array Expansion
private boolean isArrayFull() {
return length == list.length;
}
private void expandArray() {
T[] oldList = list;
int oldSize = oldList.length;
list = (T[]) new Object[2 * oldSize];
for (int i = 0; i < oldSize; i++) // copy old array elements into new array elements
list[i] = oldList[i];
}
// ADT METHODs
// Add New Elements Function
#Override
public boolean add(T element) {
int i = 0;
while (i < length && element.compareTo(list[i]) > 0) // return 0 with equal , return more than 1 if element larger than list[i] , return -1 if less
i++;
makeRoom(i + 1);
list[i] = element;
length++;
return true;
}
private void makeRoom(int index) { // accepts given index
int newIndex = index - 1;
int lastIndex = length - 1;
for (int i = lastIndex; i >= newIndex; i--)
list[i + 1] = list[i];
}
//Remove Elements Function
#Override
public T remove(int index) { // accepts given index
T result = null;
if ( index >= 1 && index <= length ) {
result = list[index - 1];
if (index < length)
removeGap(index);
length--;
}
return result;
}
private void removeGap(int index) { // accepts given index and remove the gap where the element its removed
int removedIndex = index - 1;
int lastIndex = length - 1;
for (int i = removedIndex; i < lastIndex; i++)
list[i] = list[i + 1]; // shifts elements back to remove the gap
}
// Get Element
#Override
public T get(int index) { // accepts given index and return the object
T object = null;
if ( index >= 1 && index <= length)
object = list[index - 1];
return object;
}
// Search Algorithms
#Override
public boolean search(T element) {
return binarySearch(element);
}
private boolean binarySearch(// Implementation here) {
// Implementation here
}
//To String Method
#Override
public String toString() {
String result = "";
for (int i = 0; i < length; i++)
result += list[i] + "\n";
return result;
}
}
Student Class Implementation
public class Student implements Comparable<Student>{
// Data Types
private Name name;
private char gender;
private String icNo;
private String mobileNo;
private Course course;
private int group;
private String dOB;
// Constructors
public Student() {
}
public Student(Name name, char gender, String icNo, String mobileNo, Course course, int group, String dOB) {
this.name = name;
this.gender = gender;
this.icNo = icNo;
this.mobileNo = mobileNo;
this.course = course;
this.group = group;
this.dOB = dOB;
}
public Student(Name name) {
this.name = name;
}
// setter
public void setName(Name name) {
this.name = name;
}
public void setGender(char gender) {
this.gender = gender;
}
public void setIcNo(String icNo) {
this.icNo = icNo;
}
public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}
public void setCourse(Course course) {
this.course = course;
}
public void setGroup(int group) {
this.group = group;
}
public void setdOB(String dOB) {
this.dOB = dOB;
}
// getter
public Name getName() {
return name;
}
public char getGender() {
return gender;
}
public String getIcNo() {
return icNo;
}
public String getMobileNo() {
return mobileNo;
}
public Course getCourse() {
return course;
}
public int getGroup() {
return group;
}
public String getdOB() {
return dOB;
}
#Override
public String toString() {
return "Student{" + "name=" + name + ", gender=" + gender + ", icNo=" + icNo + ", mobileNo=" + mobileNo + ", course=" + course + ", group=" + group + ", dOB=" + dOB + '}';
}
public int compareTo(Student object) { // Sort according to name if name same then sort according to gender and so on.
int c = this.name.getFullName().compareTo(object.getName().getFullName());
if(c == 0)
c = this.gender - object.getGender();
if(c == 0)
c = this.icNo.compareTo(object.getIcNo());
if(c == 0)
c = this.mobileNo.compareTo(object.getMobileNo());
if(c == 0)
c = this.group - object.getGroup();
if(c == 0)
c = this.dOB.compareTo(object.getdOB());
return c;
}
}
Course Class
public class Course {
// Data Types
private String courseCode;
private String courseName;
private double courseFee;
// Constructors
public Course() {
}
public Course(String courseCode, String courseName, double courseFee) {
this.courseCode = courseCode;
this.courseName = courseName;
this.courseFee = courseFee;
}
// setter
public void setCourseCode(String courseCode) {
this.courseCode = courseCode;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public void setCourseFee(double courseFee) {
this.courseFee = courseFee;
}
// getter
public String getCourseCode() {
return courseCode;
}
public String getCourseName() {
return courseName;
}
public double getCourseFee() {
return courseFee;
}
#Override
public String toString() {
return "CourseCode = " + courseCode + "Course Name = " + courseName + "Course Fee = " + courseFee;
}
}
Name Class
public class Name {
// Data Types
private String firstName;
private String lastName;
// Constructors
public Name() {
}
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// setter
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
// getter
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getFullName(){
return firstName + " " + lastName;
}
#Override
public String toString() {
return "Name{" + "firstName=" + firstName + ", lastName=" + lastName + '}';
}
The binary search algorithm relies on comparing a value being searched for with values in the list being searched. That's why the declaration of your class that implements the SortedListInterface is:
SortedArrayList<T extends Comparable<T>>
Note the extends Comparable<T>.
Comparable is an interface through which you can compare two objects. Hence in the search() method that you have to implement, you know that every object in the list defines the compareTo() method and you simply use that method to compare the object being searched for with individual objects in the list.
Here is a simple implementation of the binary search algorithm in the context of your project.
private T[] list; // The list to search.
private int count; // The number of non-null elements in 'list'.
public boolean search(T element) {
boolean found = false;
int lo = 0;
int hi = count - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (list[mid].compareTo(element) < 0) {
lo = mid + 1;
}
else if (list[mid].compareTo(element) > 0) {
hi = mid - 1;
}
else {
found = true;
break;
}
}
return found;
}
With a method, you have a method parameter. In the method code you use the parameter name. But when you invoke that method from other code, you provide a value which is substituted for the parameter. In the same way, the code above uses a type parameter which is substituted with the name of an actual class when you create an instance of class SortedArrayList. In your case, T is substituted with Student and class Student must implement the compareTo() method. Hence method search(), in class SortedArrayList does not need to know about the members in class Student.
So you would first create an instance of SortedArrayList like this:
SortedArrayList<Student> theList = new SortedArrayList<>();
Then you can call the search() method like this:
Student s = new Student(/* relevant parameter values */);
theList.search(s);
EDIT
I understand that you don't necessarily want to search for a Student, you may want to search for the Name of a student or a student's mobile phone number. In that case I believe you need a Comparator. Here is the code for class SortedArrayList with the addition of a Comparator
import java.util.Comparator;
import java.util.Objects;
public class SortedArrayList<T extends Comparable<T>> implements SortedListInterface<T> {
private static final int SIZE = 10;
private Comparator<? super T> comparator;
private T[] list;
private int count;
#SuppressWarnings("unchecked")
public SortedArrayList(Comparator<? super T> c) {
comparator = c;
list = (T[]) new Comparable[SIZE]; // No way to verify that 'list' only contains instances of 'T'.
/* NOTE: Following is not allowed.
list = new T[SIZE]; // Cannot create a generic array of T
*/
}
#Override
public boolean add(T element) {
Objects.requireNonNull(element, "Cannot add null element.");
boolean result = false;
if (count == 0) {
list[0] = element;
count = 1;
result = true;
}
else {
if (!isFull()) {
int i = 0;
while (list[i] != null) {
if (element.compareTo(list[i]) < 0) {
break;
}
i++;
}
if (list[i] != null) {
for (int j = count - 1; j >= i; j--) {
list[j + 1] = list[j];
}
}
list[i] = element;
count++;
result = true;
}
}
return result;
}
#Override
public T get(int index) {
checkIndex(index);
return list[index];
}
#Override
public boolean search(T element) {
if (comparator == null) {
return binarySearchComparable(element);
}
else {
return binarySearchComparator(element);
}
}
#Override
public T remove(int index) {
checkIndex(index);
T removed = list[index];
list[index] = null;
for (int i = index; i < count; i++) {
list[i] = list[i + 1];
}
count--;
list[count] = null;
return removed;
}
#Override
public void clear() {
for (int i = 0; i < count; i++) {
list[i] = null;
}
count = 0;
}
#Override
public int getLength() {
return count;
}
#Override
public boolean isEmpty() {
return count == 0;
}
#Override
public boolean isFull() {
return count == SIZE;
}
private boolean binarySearchComparable(T element) {
boolean found = false;
int lo = 0;
int hi = count - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (list[mid].compareTo(element) < 0) {
lo = mid + 1;
}
else if (list[mid].compareTo(element) > 0) {
hi = mid - 1;
}
else {
found = true;
break;
}
}
return found;
}
private boolean binarySearchComparator(T key) {
int low = 0;
int high = count - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
T midVal = list[mid];
int cmp = comparator.compare(midVal, key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return true; // key found
}
return false; // key not found.
}
private void checkIndex(int index) {
if (index < 0) {
throw new IllegalArgumentException("Negative index.");
}
if (index >= count) {
throw new IllegalArgumentException(String.format("Supplied index %d is not less than %d", index, count));
}
}
}
Here is an example Comparator for the Name of a Student
import java.util.Comparator;
public class NameComparator implements Comparator<Student> {
#Override
public int compare(Student student1, Student student2) {
int result;
if (student1 == null) {
if (student2 == null) {
result = 0;
}
else {
result = -1;
}
}
else {
if (student2 == null) {
result = 1;
}
else {
result = student1.getName().getFullName().compareTo(student2.getName().getFullName());
}
}
return result;
}
}
So in order to search the list according to any combination of Student attributes, simply implement an appropriate Comparator and pass it to the SortedArrayList class.
EDIT 2
Following your comments from November 17, 2019.
Below is code for a "name and mobile" Comparator. As I wrote in my previous Edit, you need to write an appropriate Comparator for a given combination of Student attributes.
import java.util.Comparator;
/**
* Compares {#code Student} name and mobile phone number.
*/
public class NameAndMobileComparator implements Comparator<Student> {
#Override
public int compare(Student student1, Student student2) {
int result;
if (student1 == null) {
if (student2 == null) {
result = 0;
}
else {
result = -1;
}
}
else {
if (student2 == null) {
result = 1;
}
else {
result = student1.getName().getFullName().compareTo(student2.getName().getFullName());
if (result == 0) {
result = student1.getMobileNo().compareTo(student2.getMobileNo());
}
}
}
return result;
}
}

How to find a maximum value of an index in a list of lists

I have a list of lists of objects, with each single innerlist having 3 Object elements, theoretically a String and two doubles,say a and b, in that order.
ArrayList<ArrayList<Object>> timings = new ArrayList<ArrayList<String>>()
for (int runnerno = 0; runnerno < runners; runnerno++) {
ArrayList<Object> thisrunner = new ArrayList<Object>();
thisrunner.add(sc.next()); //string
thisrunner.add(sc.nextDouble()); //double a
thisrunner.add(sc.nextDouble()); //double b
timings.add(thisrunner);
sc.nextLine();
}
How do I find out the maximum a value in my list of lists? ie. I want to find an indexed maximum.
1) Lets make a better encapsulation of your data object, call it FooBar
public class FooBar {
private String text;
private Double x;
private Double y;
public FooBar() {
}
public FooBar(String text,Double x,Double y) {
this.text = text;
this.x = x;
this.y = y;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Double getX() {
return x;
}
public void setX(Double x) {
this.x = x;
}
public Double getY() {
return y;
}
public void setY(Double y) {
this.y = y;
}
}
2) Populate a list of FooBars
List<FooBar> points = new ArrayList<FooBar>();
for( int i = 0; i < 1000; i++ ) {
FooBar f = new FooBar("Text" + i,
ThreadLocalRandom.current().nextDouble(0, 100),
ThreadLocalRandom.current().nextDouble(0, 100));
points.add(f);
}
3) Use streams.max (with Comparator)
Optional<FooBar> maxFooBar = points.stream().max(new Comparator<FooBar>() {
#Override
public int compare(FooBar o1, FooBar o2) {
return o1.getX().compareTo(o2.getX());
}
});
System.out.println("Max Point: " + maxFooBar.get().getX());
4) Or use Collections.max
FooBar maxFooBar = Collections.max(points, new Comparator<FooBar>() {
#Override
public int compare(FooBar o1, FooBar o2) {
return o1.getX().compareTo(o2.getX());
}
});
System.out.println("Max Point: " + maxFooBar.getX());
5) Or just sort the list yourself and get the first item (if sorted Descending; get last if Ascending)
points.sort(new Comparator<FooBar>() {
#Override
public int compare(FooBar o1, FooBar o2) {
return -1 * o1.getX().compareTo(o2.getX());
}
});
System.out.println("Max Point: " + points.get(0).getX());
The maximum double value of all values that are of type Double in the list of lists, like so
public static double findMax(List<List> lists) {
double max = Double.MIN_VALUE;
for (List list : lists)
for (Object o : list)
if (o instanceof Double)
if ((Double) o).doubleValue() > max)
max = ((Double) o).doubleValue();
return max;
}
what if you use Comparator interface for sorting your each individual Runner object in ascending order and the last Runner object inside timings treeSet would be always the index of maximum value
we cannot use the ArrayList since it does not have any constructor which support Comparator as an argument
public class Runner {
private String s;
public double a;
public double b;
public Runner() {}
public Runner(String s, double a, double b) {
this.s = s;
this.a = a;
this.b = b;
}
#Override
public String toString() {
return s + " " + a + " " + b;
}
}
calling class
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Calling {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int runners = 3;
TreeSet<Runner> timings = new TreeSet<>(new MyComparator());
for (int runnerno = 0; runnerno < runners; runnerno++) {
timings.add(new Runner(sc.nextLine(), Double.parseDouble(sc.nextLine()), Double.parseDouble(sc.nextLine())));
}
System.out.println(timings);
System.out.println("max value " + timings.last());
}
}
class MyComparator implements Comparator<Runner> {
#Override
public int compare(Runner o1, Runner o2) {
return Double.valueOf(o1.a).compareTo(Double.valueOf(o2.a));
}
}

Java Comparator will calculate score 1 time or several times

I need to sort a list with my custom comparator: Collections.sort(availableItems, new TextClassifyCnnComparator(citem, false))
class TextClassifyCnnComparator implements Comparator<Item> {
private Item citem;
private boolean isAsc;
public TextClassifyCnnComparator(Item citem) {
this(citem, true);
}
public TextClassifyCnnComparator(Item citem, boolean isAsc) {
this.citem = citem;
this.isAsc = isAsc;
}
private Double calcSimilarScore(Item item) {
return item.getEncodedFromCNN().dotProduct(citem.getEncodedFromCNN());
}
#Override
public int compare(Item o1, Item o2) {
if (isAsc) {
return calcSimilarScore(o1).compareTo(calcSimilarScore(o2));
}
return calcSimilarScore(o2).compareTo(calcSimilarScore(o1));
}
}
Will Java map and call calcSimilarScore 1 time for each item or it would be called several times (1 for each tuple 2 item)?
If it call several times, how can I optimize this task?
========= Update 1: ===============
I have refract my comparator in this:
class TextClassifyCnnComparator implements Comparator<Integer> {
private boolean isAsc;
private List<Double> list;
public TextClassifyCnnComparator(Item citem, List<Item> list) {
this(citem, list, true);
}
public TextClassifyCnnComparator(Item citem, List<Item> list, boolean isAsc) {
this.list = list.parallelStream().map(item -> calcSimilarScore(item, citem)).collect(Collectors.toList());
this.isAsc = isAsc;
}
private Double calcSimilarScore(Item item1, Item item2) {
return item1.getEncodedFromCNN().dotProduct(item2.getEncodedFromCNN());
}
public List<Integer> createIndexes() {
List<Integer> indexes = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
indexes.add(i); // Autoboxing
}
return indexes;
}
#Override
public int compare(Integer index1, Integer index2) {
// Autounbox from Integer to int to use as array indexes
if (isAsc)
return list.get(index1).compareTo(list.get(index2));
return list.get(index2).compareTo(list.get(index1));
}
}
...
TextClassifyCnnComparator comparator = new TextClassifyCnnComparator(citem, availableItems);
List<Integer> indexes = comparator.createIndexes();
Collections.sort(indexes, comparator);
return indexes.parallelStream().map(index -> availableItems.get(index)).collect(Collectors.toList());
I think it still can be optimized more.
There are the following optimizations:
One should use double (the "primitive" data type) instead of Double (the Object wrapper class holding a double), wherever feasible.
Of the compared citem a part can be precalculated in the constructor. (citem might even not be needed anymore as field.)
A value may be compared several times, so a cache is possible, a Map from Item to its double value.
So
class TextClassifyCnnComparator implements Comparator<Item> {
private final Item citem;
private final boolean isAsc;
private final ECNN encodedFromCNN;
private Map<Item, Double> scores = new HashMap<>();
public TextClassifyCnnComparator(Item citem) {
this(citem, true);
}
public TextClassifyCnnComparator(Item citem, boolean isAsc) {
this.citem = citem;
this.isAsc = isAsc;
encodedFromCNN = citem.getEncodedFromCNN();
}
private double calcSimilarScore(Item item) {
Double cached = scores.get(item);
if (cached != null) {
return cached;
}
double score = item.getEncodedFromCNN().dotProduct(encodedFromCNN);
scores.put(Item, score);
return score;
}
#Override
public int compare(Item o1, Item o2) {
if (isAsc) {
return calcSimilarScore(o1).compareTo(calcSimilarScore(o2));
}
return calcSimilarScore(o2).compareTo(calcSimilarScore(o1));
}
}
Or stylish in java 8:
private double calcSimilarScore(Item item) {
return scores.computeIfAbsent(item,
it -> it.getEncodedFromCNN().dotProduct(encodedFromCNN));
}

Java Arraylist<Object> want to merge the objects if they have the same property value

Java ArrayList, filled with objects called packinglistrows which hold three key values ( ISBN, PalletNumber, Quantity), along with other properties.
I have this ArrayList with all the same ISBN values in it. I want to be able to merge the items with the same PalletNumbers quantity value.
For example:
ArrayList items = [ packinglistrow( 1234, 1, 10 ), packinglistrow( 1234, 2, 5), packinglistrow( 1234, 1, 15 ) ]
After merge the [0] and [2] objects are merged as they have the same ISBN and pallet number 1. Resulting in a merged object with the updated quantity:
ArrayList items = [ packinglistrow( 1234, 1, 25 ), packinglistrow( 1234, 2, 5) ]
Was thinking loop over and compare and add the different types to new ArrayList then loop over and merge, but there must be a neater way of doing this?
Thanks.
First create a class to handle this datas. There's two important points to note. The equals and hashcode method are only based with the isbn and palletNumber values and there is a merge method that returns a new instance of PackingListRow with the quantities between this and an other instance you give as parameter.
class PackingListRow {
private final String isbn;
private final int palletNumber;
private final int quantity;
public PackingListRow(String isbn, int palletNumber, int quantity) {
this.isbn = isbn;
this.palletNumber = palletNumber;
this.quantity = quantity;
}
public String getIsbn() {
return isbn;
}
public int getPalletNumber() {
return palletNumber;
}
public int getQuantity() {
return quantity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackingListRow that = (PackingListRow) o;
return Objects.equals(palletNumber, that.palletNumber) &&
Objects.equals(isbn, that.isbn);
}
#Override
public int hashCode() {
return Objects.hash(isbn, palletNumber);
}
#Override
public String toString() {
return "PackingListRow{" +
"isbn='" + isbn + '\'' +
", palletNumber=" + palletNumber +
", quantity=" + quantity +
'}';
}
public PackingListRow merge(PackingListRow other) {
assert(this.equals(other));
return new PackingListRow(this.isbn, this.palletNumber, this.quantity + other.quantity);
}
}
Once you have that, you just need to create another new list that is initially empty. It will contains the merged values. For each instance in the initial list, you check whether it is already in the merged list. If yes, you modify the existing instance by calling merge, otherwise you just append it to the list. We end up with the following algorithm:
List<PackingListRow> list =
Arrays.asList(new PackingListRow("1234", 1, 10), new PackingListRow("1234", 2, 5), new PackingListRow("1234", 1, 15));
List<PackingListRow> mergedList = new ArrayList<>();
for(PackingListRow p : list) {
int index = mergedList.indexOf(p);
if(index != -1) {
mergedList.set(index, mergedList.get(index).merge(p));
} else {
mergedList.add(p);
}
}
System.out.println(mergedList);
Which outputs:
[PackingListRow{isbn='1234', palletNumber=1, quantity=25}, PackingListRow{isbn='1234', palletNumber=2, quantity=5}]
With Java 8, I would maybe use a different strategy (at least you show there are multiple ways to solve the problem). I would create a static class that does the grouping for me:
class PackingListRow {
private final String isbn;
private final int palletNumber;
private final int quantity;
static class GroupPacking {
private final String isbn;
private final int palletNumber;
public GroupPacking(PackingListRow p) {
this.isbn = p.isbn;
this.palletNumber = p.palletNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupPacking that = (GroupPacking) o;
return Objects.equals(palletNumber, that.palletNumber) &&
Objects.equals(isbn, that.isbn);
}
#Override
public int hashCode() {
return Objects.hash(isbn, palletNumber);
}
}
....
public PackingListRow merge(PackingListRow other) {
assert (new GroupPacking(other).equals(new GroupPacking(this)));
return new PackingListRow(this.isbn, this.palletNumber, this.quantity + other.quantity);
}
}
Then you can use the Stream API. Given the original list, you get a Stream<PackingListRow> from which you collect the elements into a Map according by their GroupPacking instances (the keys). The value is simply the current PackingListRow instance. If you have two instances with the same GroupPacking value (according to equals/hashcode), you merge them. You finally get the values() of the map.
List<PackingListRow> mergedList =
new ArrayList<>(list.stream().collect(toMap(PackingListRow.GroupPacking::new, p -> p, PackingListRow::merge)).values());
Here you have working example (Java8 + Google Guava):
package com.rgrebski.test;
import com.google.common.base.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimaps;
import org.assertj.core.api.Assertions;
import org.testng.annotations.*;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PackingListRowMergeTest {
#Test
public void test() {
//given
List<PackingListRow> packingListRow = ImmutableList.of(
new PackingListRow(1234, 1, 10),
new PackingListRow(1234, 2, 5),
new PackingListRow(1234, 1, 15)
);
//when
List<PackingListRow> mergedPackingListRow = Multimaps.index(packingListRow, groupByPaletNumbers())
.asMap()
.values()
.stream()
.map(packingListRowCollectionToSinglePackingListRowWithQuantitySum())
.collect(Collectors.toList());
//then
Assertions.assertThat(mergedPackingListRow).containsExactly(
new PackingListRow(1234, 1, 25),
new PackingListRow(1234, 2, 5)
);
}
protected java.util.function.Function<Collection<PackingListRow>, PackingListRow> packingListRowCollectionToSinglePackingListRowWithQuantitySum() {
return new java.util.function.Function<Collection<PackingListRow>, PackingListRow>() {
#Override
public PackingListRow apply(Collection<PackingListRow> packingListRows) {
int quantitySum = packingListRows.stream().flatMapToInt(packingListRow -> IntStream.of(packingListRow.getQuantity())).sum();
PackingListRow firstPackingListRow = packingListRows.stream().findFirst().get();
return new PackingListRow(firstPackingListRow.getIsbn(), firstPackingListRow.getPaletNumber(), quantitySum);
}
};
}
private Function<? super PackingListRow, Integer> groupByPaletNumbers() {
return new Function<PackingListRow, Integer>() {
#Override
public Integer apply(PackingListRow input) {
return input.getPaletNumber();
}
};
}
private static class PackingListRow {
private int isbn;
private int paletNumber;
private int quantity;
public PackingListRow(int isbn, int paletNumber, int quantity) {
this.isbn = isbn;
this.paletNumber = paletNumber;
this.quantity = quantity;
}
public int getIsbn() {
return isbn;
}
public int getPaletNumber() {
return paletNumber;
}
public int getQuantity() {
return quantity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackingListRow that = (PackingListRow) o;
return Objects.equal(this.isbn, that.isbn) &&
Objects.equal(this.paletNumber, that.paletNumber) &&
Objects.equal(this.quantity, that.quantity);
}
#Override
public int hashCode() {
return Objects.hashCode(isbn, paletNumber, quantity);
}
#Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("isbn", isbn)
.add("paletNumber", paletNumber)
.add("quantity", quantity)
.toString();
}
}
}
It seems reasonable to create an object just for ISBN & pallet number, but keep the quantity separately as you consider them equal if ISBN & pallet number are equal. So the object might look like this:
public class PackingListRow {
private final String isbn;
private final int palletNumber;
public PackingListRow(String isbn, int palletNumber) {
this.isbn = isbn;
this.palletNumber = palletNumber;
}
#Override
public int hashCode() {
return palletNumber * 31 + ((isbn == null) ? 0 : isbn.hashCode());
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PackingListRow other = (PackingListRow) obj;
if (isbn == null) {
if (other.isbn != null)
return false;
} else if (!isbn.equals(other.isbn))
return false;
if (palletNumber != other.palletNumber)
return false;
return true;
}
#Override
public String toString() {
return isbn+":"+palletNumber;
}
}
After that you can store the results in the Map object and add items via method like this:
public static void addItem(Map<PackingListRow, Integer> items, PackingListRow row,
int quantity) {
Integer oldQuantity = items.get(row);
items.put(row, oldQuantity == null ? quantity : quantity+oldQuantity);
}
Or much simpler if you are using Java 8:
public static void addItem(Map<PackingListRow, Integer> items, PackingListRow row,
int quantity) {
items.merge(row, quantity, Integer::sum);
}
Testing on your sample data:
public static void main(String[] args) {
Map<PackingListRow, Integer> items = new HashMap<PackingListRow, Integer>();
addItem(items, new PackingListRow("1234", 1), 10);
addItem(items, new PackingListRow("1234", 2), 5);
addItem(items, new PackingListRow("1234", 1), 15);
System.out.println(items);
}
The output is:
{1234:1=25, 1234:2=5}

Organizing array list of objects JAVA

I'm having troubling organizing my list of employees. I just need to organize them according to their employee type (first two letters). Each object starts with the employee code which are the first two letters. This is what I need to separate the position types but for some reason I can't grab them.
Here is the file that I am creating the objects out of and storing them in the arrays.
PW_1234,James,Bond,01/02/10,1,10 PW_1235,John,Brown,02/03/10,2,10.5
PW_1236,Howard,Johnson,03/04/10,3,11
PW_1237,Francis,Themule,04/05/11,4,10.75
PW_1238,Mathew,Lewis,05/06/11,1,12.75
PW_1239,Mark,Bixton,05/13/11,2,13
PW_1242,Sarah,Glover,05/14/11,1,13.75 PW_1245,John,Doe,05/15/11,4,10.5
PW_1245,Mary,Doe,05/15/11,4,10.5
TL_1248,Abel,English,05/16/11,3,16.5,0.01,100,89
TL_1251,Eustis,Clauser,05/17/11,2,16,0.02,100,9
SU_1254,Henry,Hollowman,05/18/11,1,40000,0.01
PW_1240,Luke,Sailor,01/22/12,3,14.5 PW_1243,Jane,Baker,01/23/12,2,14
PW_1243,Jane,Baker,01/23/12,2,14
TL_1246,David,Brief,01/24/12,1,14.75,0.01,100,57
PW_1246,David,Doson,01/24/12,1,14.75
TL_1249,Baker,Anderson,01/25/12,4,11.5,0.01,100,100
TL_1252,Frank,Donson,01/26/12,3,17.5,0.02,100,39
SU_1255,Issac,Asimov,01/27/12,2,43000,0.02
SU_1256,Issac,Shoreman,01/28/12,3,39000,0.01
SU_1257,Issac,Roberts,01/29/12,4,35500,0.01
PW_1241,John,Candy,11/23/13,4,9.5 PW_1244,Kate,Smith,11/24/13,3,15.5
PW_1244,Kate,Handle,11/24/13,3,15.5
TL_1247,Samual,Dempky,11/25/13,2,15,0.01,100,10
TL_1250,Charley,Boman,11/26/13,1,15.75,0.01,100,50
TL_1253,George,Fritzmen,11/27/13,4,12.5,0.02,100,27
Here is the code:
private String makeEmployeeList()
{
String list = "";
for(int i=0; i < employees.length; i++)
{
list += "\n"+employees[i].toString();
if(employees[i]substring(0,2).equals("SU"))
{
list += "\n"+employees[i].toString();
}
}
return list;
}
**Here is how the employees array is created:
private Employee[] employees;
**Here is how everything is loaded into it.
public void loadEmployeesFromFile(String fileName)
{
File inFile = new File(fileName);
if(inFile.exists()) // MAKE SURE FILE EXISTS
{
try
{
BufferedReader inReader = new BufferedReader(new FileReader(inFile));
inReader.mark(32000);
String inLine = inReader.readLine();
//************************************
// Counting rows to set array size
//************************************
int rowCount = 0;
while (inLine != null && !inLine.equals(""))
{
rowCount++;
inLine = inReader.readLine();
}
inReader.reset();
//*******************
// re-reading data
//*******************
this.employees = new Employee[rowCount];
for(int rowIndex = 0;rowIndex < rowCount; rowIndex++)
{
inLine = inReader.readLine();
Scanner employeeScanner = new Scanner(inLine).useDelimiter(",");
String workerType = employeeScanner.next();
String firstName = employeeScanner.next();
String lastName = employeeScanner.next();
String hireDate = employeeScanner.next();
int shift = employeeScanner.nextInt();
if(workerType.substring(0,2).equals("PW"))
{
double pay = employeeScanner.nextDouble();
employees[rowIndex]= new ProductionWorker(workerType, firstName, lastName, hireDate, shift, pay);
}
else if(workerType.substring(0,2).equals("TL"))
{
double pay = employeeScanner.nextDouble();
double bonusRate = employeeScanner.nextDouble();
int reqHours = employeeScanner.nextInt();
int recHours = employeeScanner.nextInt();
employees[rowIndex]= new TeamLeader(workerType, firstName, lastName, hireDate, shift, pay, bonusRate, reqHours, recHours);
}
else if(workerType.substring(0,2).equals("SU"))
{
double salary = employeeScanner.nextDouble();
double bonusRate = employeeScanner.nextDouble();
employees[rowIndex]= new ShiftSupervisor(workerType, firstName, lastName, hireDate, shift, salary, bonusRate );
}
}
return;
}catch(IOException ioe)
{
System.err.print("\nTrouble reading employee file: "+fileName);
}
}
JOptionPane.showMessageDialog(null, "\nFile Name does not exist!\n Process terminating!");
System.exit(0);
}
private String makeEmployeeList(){
StringBuilder sbSU = null;
for(int i=0; i < employees.length; i++)
{
sbSU = new StringBuilder();
if(employees[i].substring(0,2).equals("SU"))
{
sbSU.append(employees[i].toString());
}
}
return sbSU.toString();
}
First of all, you missed a dot after emplyees[i] subsrting
As string is an immutable object, I suggest you use StringBuilder and its append method instead of +=. and use its toString() method to convert StringBuilder to a String. You also need to override your Employees's toString method.
to sort the employees in an array, you need to implements Comparable or Comparator interface so that the Array knows which criteria to use when sorting your employees, in your case it is to compare the employee's type
As you are using JOptionPane you can use html inside to give it format. Make an Employee class and make it's natural order by type, or you can use a Comparator if you don't want to use Comparable
I made a complete example for you.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;
public class Employee implements Comparable<Employee> {
private String type;
private String name;
public Employee(String type, String name) {
super();
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.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;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
public int compareTo(Employee o) {
if (this.type.equals(o.type)) {
return name.compareTo(o.name);
}
return type.compareTo(o.type);
}
public static void main(String[] args) {
List<Employee> employees = new ArrayList<Employee>();
employees.add(new Employee("CA","John"));
employees.add(new Employee("CA", "Suzy"));
employees.add(new Employee("TA","Malcom"));
employees.add(new Employee("AA","Rose"));
// Sort the list by type as its natural order or use proper Comparator
Collections.sort(employees);
StringBuilder sb = new StringBuilder();
sb.append("<html><table><tr><td>Type</td><td>Name</td></tr>");
for (Employee e : employees) {
sb.append("<tr>");
sb.append("<td> ").append(e.getType()).append("</td>");
sb.append("<td> ").append(e.getName()).append("</td>");
sb.append("</tr>");
}
sb.append("</table></html>");
JOptionPane.showMessageDialog(null, sb);
}
}
Output:

Categories