This is my assignment, I'm working on the step 3:
I need to implement a method called getItems() that takes a boolean value and returns a new ArrayList with items having the property that match the given value.
So far When I print it out it just shows the whole list in the array list, not only the objects having true property.
My code:
import java.util.ArrayList;
public class Shoppinglist9_6_pt2 {
ArrayList<Item> shoppingList = new ArrayList<>();
public Shoppinglist9_6_pt2(ArrayList<Item> shoppingList) {
this.shoppingList = shoppingList;
}
public void showList() {
for ( Item item : shoppingList){
System.out.printf("\nItem:%s Ct:%s", item.getName(),
item.getCt());
}
}
public ArrayList<Item> getItems(boolean gotIt) {
gotIt = true;
for (Item item : shoppingList){
if(item.getGotIt() == gotIt){
showList();
}else{
gotIt = false;
}
}
// Todo: return an ArrayList of item that
// match the gotIt true or false value.
// For example if set to True, then return
// an ArrayList of Item with gotIt=True.
return new ArrayList<Item>();
}}
public class Item {
private String name;
private int ct;
private boolean gotIt;
}
public Item(String name, int ct,boolean gotIt) {
this.name = name;
this.ct = ct;
this.gotIt = gotIt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCt() {
return ct;
}
public void setCt(int ct) {
this.ct = ct;
}
public boolean getGotIt(){
return gotIt;
}
public void setGotIt(boolean gotIt) {
this.gotIt = gotIt;
}
#Override
public String toString() {
return "Item :" +
"name='" + name + '\'' +
", ct=" + ct +
"Got it=" + gotIt;
}
}
This is my main:
public class inClass_Shopping_9_6_pt2 {
public static void main(String[] args) {
Shoppinglist9_6_pt2 sl = new Shoppinglist9_6_pt2();
sl.addItem( "Banana", 6,true);
sl.addItem("Coconut", 2,false);
sl.addItem("Apple", 12,true);
sl.getItems(true);
}
}
Also, you might observe the method gotIt() is grayed out & says parameters can be converted to a local varaible, same with the return. 'Item' in ArrayList<Items> is grayed out and says explicit type argument item can be replaced with <>, but this is the template my professor had sent us, and we need to follow it.
You have the new instance variable, you have the getters and setters, just the getItems method does not yet do the job...
You want to get a list with all the entries of the shopping list that have the attribute gotIt either true or false depending on the gotIt variable. How can you use the gotIt variable to get all the items you want?
Normally one would choose different names for the parameter of getItems, so it doesn't collide with the attributes name
You need to perform the following steps:
create a new ArrayList.
iterate through the sopping list and add each item with gotIt property matches the provided boolean value into the newly created ArrayList.
return the list.
That's how it might look like:
public ArrayList<Item> getItems(boolean gotIt) {
ArrayList<Item> items = new ArrayList<>();
for (Item item : shoppingList) {
if (item.getGotIt() == gotIt) {
items.add(item);
}
}
return items;
}
Note: although according to the assignment requirements you have to use ArrayList as a type in your code, it's not good practice to make the code dependent on concrete implementations. See What does it mean to "program to an interface"?
I know I have join party late, and my answer is not different from Alexandar.
I'm not offering a lot of assistance, still you can try using lambdas.
For instance
public ArrayList<Item> getItems(boolean gotIt) {
ArrayList<Item> items = new ArrayList<>();
shoppingList.forEach(item -> {
if(item.getGotIt() == gotIt) items.add(item);
});
return items;
}
For print,
sl.getItems(false).forEach(System.out::println);
Java noob here. I am trying to build a simple point of sale checkout application. I have a simple item database in mysql shown in the pic below.
I have an Item class built as below with getters and setters for each column
public class Item {
private String itemName;
private double unitPrice;
private boolean weightReqd;
private boolean quanReqd;
private boolean recalled;
private boolean ageRest;
private boolean eCpn;
private double eCpnAmt;
public Item(){}
public Item(String itemName, double unitPrice, boolean weightReqd, boolean quanReqd, boolean recalled,
boolean ageRest, boolean eCpn, double eCpnAmt){
this.itemName = itemName;
this.unitPrice = unitPrice;
this.weightReqd = weightReqd;
this.quanReqd = quanReqd;
this.recalled = recalled;
this.ageRest = ageRest;
this.eCpn = eCpn;
this.eCpnAmt = eCpnAmt;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
public boolean isWeightReqd() {
return weightReqd;
}
public void setWeightReqd(boolean weightReqd) {
this.weightReqd = weightReqd;
}
public boolean isQuanReqd() {
return quanReqd;
}
public void setQuanReqd(boolean quanReqd) {
this.quanReqd = quanReqd;
}
public boolean isRecalled() {
return recalled;
}
public void setRecalled(boolean recalled) {
this.recalled = recalled;
}
public boolean isAgeRest() {
return ageRest;
}
public void setAgeRest(boolean ageRest) {
this.ageRest = ageRest;
}
public boolean iseCpn() {
return eCpn;
}
public void seteCpn(boolean eCpn) {
this.eCpn = eCpn;
}
public double geteCpnAmt() {
return eCpnAmt;
}
public void seteCpnAmt(double eCpnAmt) {
this.eCpnAmt = eCpnAmt;
}
}
And then i am using setters from the Item class to add the records to a List of Items (List).
public class CreateItemList {
private static Connection conn = null;
public List<Item> itemList = new ArrayList<Item>();
public void createDbConnection() throws ClassNotFoundException, SQLException {
String myUrl = "jdbc:mysql://localhost:3306/itemdb?autoReconnect=true&useSSL=false";
conn = DriverManager.getConnection(myUrl, "******", "*********");
}
public void addItemsToList() throws SQLException, ClassNotFoundException {
String query = "SELECT * FROM item_source";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(query);
try
{
while (rs.next())
{
Item item = new Item();
item.setItemName(rs.getString("Item Name"));
item.setUnitPrice(rs.getDouble("Unit Price"));
item.setWeightReqd(rs.getBoolean("Weight Reqd"));
item.setQuanReqd(rs.getBoolean("Quan Reqd"));
item.setRecalled(rs.getBoolean("Recall"));
item.setAgeRest(rs.getBoolean("Age Rest"));
item.seteCpn(rs.getBoolean("Ecpn"));
item.seteCpnAmt(rs.getDouble("Ecpn Amt"));
itemList.add(item);
}
}
catch (Exception e)
{
System.err.println("Got an exception!");
System.err.println(e);
}
st.close();
}
}
Now the next thing i want to do is create exception handler methods. So if the user enters an item that is flagged as "true" for weight required, i want the is WeightReqd method to return true. But I'm struggling on this. I'm not sure how to return the boolean value for the item scanned, specifically for the Weight Reqd field.
public class ExceptionsHandler {
public boolean isWeightReqd(List<Item> itemList){
//HOW DO I RETURN THE BOOLEAN VALUE FOR THE WEIGHT REQD FIELD FROM THE LIST??
return false;
}
Would really appreciate the help! Can also take instructions on the general structure of this program, if there are better ways to do it!
You need to call your ExceptionHandler class for a specific item scanned. You dont need to pass the whole list in your isWeightReqd() method. Only pass your Item. Change your method signature to: public boolean isWeightReqd(Item itemList) and should get it.
I think this is a case where a hashmap might make more sense than an arraylist, using the name of the item as a key. Regardless once you have found that item all you need to call is
boolean b = item.isWeightReqd();
As per my understanding from your inputs in the comments, the user will enter the Item Name of an item and you want to return whether weight is required or not. So, the method signature would have the list of items from the DB and the item name entered by the user.
Option 1:
You can stream the contents of the list, find the item whose itemName matches the one entered by the user and return its weightReqd.
public boolean isWeightReqd(List<Item> itemList, String itemScanned) {
return itemList.stream()
.filter(item -> item.getItemName().equals(itemScanned))
.findFirst()
.map(Item::isWeightReqd)
.orElseThrow(() -> new RuntimeException("The item " + itemScanned
+ " does not exist"));
}
However, the worst-case running time for this is O(n) if the item scanned is not present or if it is the last one in the list.
Option 2:
If you want an O(1) lookup, you can convert the List<Item> after fetching it from the DB as a Map whose key is the itemName and value is the Item itself.
Map<String, Item> items = itemList.stream()
.collect(Collectors.toMap(Item::getItemName, Function.identity()));
public boolean isWeightReqd(List<Item> itemList, String itemScanned) {
if (items.containsKey(itemScanned)) {
return items.get(itemScanned).isWeightReqd();
}
throw new RuntimeException("The item " + itemScanned + " does not exist");
}
//Or by using Optional
Optional.ofNullable(items.get(itemScanned))
.map(Item::isWeightReqd)
.orElseThrow(() -> new RuntimeException("The item " + itemScanned
+ " does not exist"));
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
my current program is a pupil library system, I have my array lists, menus and methods which all work. My problem is i need the arrays to be reading from the superclass LoanBook which takes in overrides from the Subclasses (Fiction and NonFiction).
As you can see from the AddBook method, it takes in details of the book and stores to an array list.
My Question :
I need to add the option Fiction or Non-Fiction but i need the arraylist take take property's from the Superclass and SubClasses. Can i get some help please.
Im happy to answer any questions you may have or provide more information.
Main Class
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Scanner;
public class Main{
static Scanner keyboard = new Scanner(System.in);
static boolean run = true;
static Formatter x;
public static void main(String[]args){
LoanBook myBook = new LoanBook();
while (run){ // this while statement allows the menu to come up again
int answer = 0;
boolean isNumber;
do{ // start of validation
System.out.println("1. Add book");
System.out.println("2. Display the books available for loan");
System.out.println("3. Display the books currently on loan");
System.out.println("4. Make a book loan");
System.out.println("5. Return book ");
System.out.println("6 Write book details to file");
if (keyboard.hasNextInt()){ // I need to consider putting in a =>1 <=6
answer = keyboard.nextInt();
isNumber = true;
} else {
System.out.print(" You must enter a number from the menu to continue. \n");
isNumber = false;
keyboard.next(); // clears keyboard
}
}
while (!(isNumber));
switch (answer){
case 1:
addBook();
break;
case 2:
viewAll();
break;
case 3:
booksOnLoan();
break;
case 4:
loanBook();
break;
case 5:
returnBook();
break;
case 6:
writeToFile();
break;
case 7:
break;
}
}
}
static List<String>pupilName = new ArrayList<String>();
static List<String>issueDate = new ArrayList<String>();
static List<String>bookTitle = new ArrayList<String>();
static List<String>bookAuthor = new ArrayList<String>();
static List bookOnloan = new ArrayList<Boolean>();
public static void viewAll(){
System.out.println("\n");
for (int x = 0; x < bookTitle.size();x++){
int counter = x+1;
System.out.println("BookID:" +counter + "\n " + bookTitle.get(x) + " - " + bookAuthor.get(x)+" " + bookOnloan.get(x));
}
}
public static void booksOnLoan(){
System.out.println("\n");
for (int x = 0; x < pupilName.size();x++){
if (bookOnloan.contains(true)){
int counter = x+1;
System.out.println("BookID:" +counter + "\n "+"Pupil name: " + pupilName.get(x)
+"\n Book Title: "+ bookTitle.get(x) + " by " + bookAuthor.get(x)+" " + bookOnloan.get(x)+ "\n Issued: "+ issueDate.get(x)) ;
}
}
}
public static void addBook(){
System.out.println("Please enter the book title: ");
String newTitle = keyboard.next();
bookTitle.add(newTitle);
System.out.println("Please enter the book author");
String newAuthor = keyboard.next();
bookAuthor.add(newAuthor);
bookOnloan.add(false);
System.out.println("\n Your book: "+ bookTitle.get(bookTitle.size()-1)+ " has been added to the library" + "\n");
}
public static void loanBook(){
viewAll();
System.out.println("Please choose the BookID you would like to issue: ");
int issue = keyboard.nextInt()-1;
if (issue > 10){
System.out.println("Invalid book selection");
}
else {
bookOnloan.set(issue,true);
System.out.println("Please enter pupil name: ");
String newPupil = keyboard.next();
pupilName.add(newPupil);
System.out.println("Please enter date of issue: ");
String newIssue = keyboard.next();
issueDate.add(newIssue);
}
}
public static void returnBook(){
// booksOnLoan();
System.out.println("Please choose the BookID you would like to return: ");
int issue = keyboard.nextInt()-1;
if (issue > 10){
System.out.println("Invalid book selection");
}
else {
bookOnloan.set(issue,false);
Next is my Superclass
public class LoanBook {
private int bookID;
private String title,author,name,date;
boolean onLoan;
private static int count = 0;
static List<String> bookTitle = new ArrayList<String>();
static List<String>bookAuthor = new ArrayList<String>();
static List<String> pupilName = new ArrayList<String>();
static List<String>issueDate = new ArrayList<String>();
static List bookOnloan = new ArrayList<Boolean>();
public LoanBook(String title,String author){ //constructor
this.bookID = count;
this.author = author;
this.title = title;
bookOnloan.add(false);
count++;
}
public void setTitle(String title){
bookTitle.set(1,title);
}
public String getTitle(){
return bookTitle.toString();
}
public void setAuthor(String author){
bookTitle.set(1,author);
}
public String getAuthor(){
return bookAuthor.toString();
}
public String getName(){
return pupilName.toString();
}
public void setName(String name){
pupilName.set(1,name);
}
public String getDate(){
return issueDate.toString();
}
public void setDate(String date){
issueDate.set(1,date);
}
public Boolean getOnloan(){
return bookOnloan.add(false);
}
public void setOnLoan(Boolean onLoan){
bookOnloan.add(false);
}
}
Next my subclasses
public class Fiction extends LoanBook {
private String type;
public Fiction(){
}
public Fiction(String title,String author, String type){
super(title,author); //calls constructor of the superclass
this.type = type;
}
public void setType(String type){
type = "Fiction";
}
public String getType(){
return type;
}
public String toString(){
return super.toString() + " The book type is: " + getType()+"\n";
}
}
and the other subclasss
public class NonFiction extends LoanBook {
private String type;
public NonFiction(){
}
public NonFiction(String title,String author, String type){
super(title,author); //calls constructor of the superclass
this.type = type;
}
public void setType(String type){
type = "Fiction";
}
public String getType(){
return type;
}
public String toString(){
return super.toString() + " The book type is: " + getType()+"\n";
}
}
Your whole program structure is broken from your over-use of static fields to your mis-use of inheritance, to your combining the concepts of a Book and a Book collection all in one class.
Suggestions:
Don't mix your Book class with your Book collection. This looks to be the primary problem with your code.
Start with just a Book class. It should contain no lists at all.
You can have FictionBook and NonFictionBook extend Book if so desired.
Or you could simply give Book a boolean field, fiction, and set it to true or false depending on the needs.
Create a LoanBook class that holds List of Books.
Don't use inheritance unless a true "is-a" relationship exists. Your code does not satisfy this mainly due to your first problem, your mixing your Book class together with your Book Library code, which forces your Fiction book and your non-Fiction book to inherit library code which is not only not needed, but really detrimental.
Avoid use of static anythings, unless they are there for a specific static purpose.
You will likely be best served by trashing your current code and re-starting over.
I have a list of "Report" objects with three fields (All String type)-
ReportKey
StudentNumber
School
I have a sort code goes like-
Collections.sort(reportList, new Comparator<Report>() {
#Override
public int compare(final Report record1, final Report record2) {
return (record1.getReportKey() + record1.getStudentNumber() + record1.getSchool())
.compareTo(record2.getReportKey() + record2.getStudentNumber() + record2.getSchool());
}
});
For some reason, I don't have the sorted order. One advised to put spaces in between fields, but why?
Do you see anything wrong with the code?
(originally from Ways to sort lists of objects in Java based on multiple fields)
Original working code in this gist
Using Java 8 lambda's (added April 10, 2019)
Java 8 solves this nicely by lambda's (though Guava and Apache Commons might still offer more flexibility):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Thanks to #gaoagong's answer below.
Note that one advantage here is that the getters are evaluated lazily (eg. getSchool() is only evaluated if relevant).
Messy and convoluted: Sorting by hand
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
int sizeCmp = p1.size.compareTo(p2.size);
if (sizeCmp != 0) {
return sizeCmp;
}
int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);
if (nrOfToppingsCmp != 0) {
return nrOfToppingsCmp;
}
return p1.name.compareTo(p2.name);
}
});
This requires a lot of typing, maintenance and is error prone. The only advantage is that getters are only invoked when relevant.
The reflective way: Sorting with BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Obviously this is more concise, but even more error prone as you lose your direct reference to the fields by using Strings instead (no typesafety, auto-refactorings). Now if a field is renamed, the compiler won’t even report a problem. Moreover, because this solution uses reflection, the sorting is much slower.
Getting there: Sorting with Google Guava’s ComparisonChain
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();
// or in case the fields can be null:
/*
return ComparisonChain.start()
.compare(p1.size, p2.size, Ordering.natural().nullsLast())
.compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast())
.compare(p1.name, p2.name, Ordering.natural().nullsLast())
.result();
*/
}
});
This is much better, but requires some boiler plate code for the most common use case: null-values should be valued less by default. For null-fields, you have to provide an extra directive to Guava what to do in that case. This is a flexible mechanism if you want to do something specific, but often you want the default case (ie. 1, a, b, z, null).
And as noted in the comments below, these getters are all evaluated immediately for each comparison.
Sorting with Apache Commons CompareToBuilder
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();
}
});
Like Guava’s ComparisonChain, this library class sorts easily on multiple fields, but also defines default behavior for null values (ie. 1, a, b, z, null). However, you can’t specify anything else either, unless you provide your own Comparator.
Again, as noted in the comments below, these getters are all evaluated immediately for each comparison.
Thus
Ultimately it comes down to flavor and the need for flexibility (Guava’s ComparisonChain) vs. concise code (Apache’s CompareToBuilder).
Bonus method
I found a nice solution that combines multiple comparators in order of priority on CodeReview in a MultiComparator:
class MultiComparator<T> implements Comparator<T> {
private final List<Comparator<T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
Collections.sort(list, new MultiComparator<T>(comparators));
}
}
Ofcourse Apache Commons Collections has a util for this already:
ComparatorUtils.chainedComparator(comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Do you see anything wrong with the code?
Yes. Why are you adding the three fields together before you compare them?
I would probably do something like this: (assuming the fields are in the order you wish to sort them in)
#Override public int compare(final Report record1, final Report record2) {
int c;
c = record1.getReportKey().compareTo(record2.getReportKey());
if (c == 0)
c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
if (c == 0)
c = record1.getSchool().compareTo(record2.getSchool());
return c;
}
I'd make a comparator using Guava's ComparisonChain:
public class ReportComparator implements Comparator<Report> {
public int compare(Report r1, Report r2) {
return ComparisonChain.start()
.compare(r1.getReportKey(), r2.getReportKey())
.compare(r1.getStudentNumber(), r2.getStudentNumber())
.compare(r1.getSchool(), r2.getSchool())
.result();
}
}
This is an old question so I don't see a Java 8 equivalent. Here is an example for this specific case.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Compares multiple parts of the Report object.
*/
public class SimpleJava8ComparatorClass {
public static void main(String[] args) {
List<Report> reportList = new ArrayList<>();
reportList.add(new Report("reportKey2", "studentNumber2", "school1"));
reportList.add(new Report("reportKey4", "studentNumber4", "school6"));
reportList.add(new Report("reportKey1", "studentNumber1", "school1"));
reportList.add(new Report("reportKey3", "studentNumber2", "school4"));
reportList.add(new Report("reportKey2", "studentNumber2", "school3"));
System.out.println("pre-sorting");
System.out.println(reportList);
System.out.println();
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
System.out.println("post-sorting");
System.out.println(reportList);
}
private static class Report {
private String reportKey;
private String studentNumber;
private String school;
public Report(String reportKey, String studentNumber, String school) {
this.reportKey = reportKey;
this.studentNumber = studentNumber;
this.school = school;
}
public String getReportKey() {
return reportKey;
}
public void setReportKey(String reportKey) {
this.reportKey = reportKey;
}
public String getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
#Override
public String toString() {
return "Report{" +
"reportKey='" + reportKey + '\'' +
", studentNumber='" + studentNumber + '\'' +
", school='" + school + '\'' +
'}';
}
}
}
If you want to sort by report key, then student number, then school, you should do something like this:
public class ReportComparator implements Comparator<Report>
{
public int compare(Report r1, Report r2)
{
int result = r1.getReportKey().compareTo(r2.getReportKey());
if (result != 0)
{
return result;
}
result = r1.getStudentNumber().compareTo(r2.getStudentNumber());
if (result != 0)
{
return result;
}
return r1.getSchool().compareTo(r2.getSchool());
}
}
This assumes none of the values can be null, of course - it gets more complicated if you need to allow for null values for the report, report key, student number or school.
While you could get the string concatenation version to work using spaces, it would still fail in strange cases if you had odd data which itself included spaces etc. The above code is the logical code you want... compare by report key first, then only bother with the student number if the report keys are the same, etc.
I suggest to use Java 8 Lambda approach:
List<Report> reportList = new ArrayList<Report>();
reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
Sorting with multiple fields in Java8
package com.java8.chapter1;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static java.util.Comparator.*;
public class Example1 {
public static void main(String[] args) {
List<Employee> empList = getEmpList();
// Before Java 8
empList.sort(new Comparator<Employee>() {
#Override
public int compare(Employee o1, Employee o2) {
int res = o1.getDesignation().compareTo(o2.getDesignation());
if (res == 0) {
return o1.getSalary() > o2.getSalary() ? 1 : o1.getSalary() < o2.getSalary() ? -1 : 0;
} else {
return res;
}
}
});
for (Employee emp : empList) {
System.out.println(emp);
}
System.out.println("---------------------------------------------------------------------------");
// In Java 8
empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
empList.stream().forEach(System.out::println);
}
private static List<Employee> getEmpList() {
return Arrays.asList(new Employee("Lakshman A", "Consultent", 450000),
new Employee("Chaitra S", "Developer", 250000), new Employee("Manoj PVN", "Developer", 250000),
new Employee("Ramesh R", "Developer", 280000), new Employee("Suresh S", "Developer", 270000),
new Employee("Jaishree", "Opearations HR", 350000));
}
}
class Employee {
private String fullName;
private String designation;
private double salary;
public Employee(String fullName, String designation, double salary) {
super();
this.fullName = fullName;
this.designation = designation;
this.salary = salary;
}
public String getFullName() {
return fullName;
}
public String getDesignation() {
return designation;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return "Employee [fullName=" + fullName + ", designation=" + designation + ", salary=" + salary + "]";
}
}
If the StudentNumber is numeric it will not be sorted numeric but alphanumeric.
Do not expect
"2" < "11"
it will be:
"11" < "2"
Use Comparator interface with methods introduced in JDK1.8: comparing and thenComparing, or more concrete methods: comparingXXX and thenComparingXXX.
For example, if we wanna sort a list of persons by their id firstly, then age, then name:
Comparator<Person> comparator = Comparator.comparingLong(Person::getId)
.thenComparingInt(Person::getAge)
.thenComparing(Person::getName);
personList.sort(comparator);
If you want to sort based on ReportKey first then Student Number then School, you need to compare each String instead of concatenating them. Your method might work if you pad the strings with spaces so that each ReportKey is the same length and so on, but it is not really worth the effort. Instead just change the compare method to compare the ReportKeys, if compareTo returns 0 then try StudentNumber, then School.
I had the same issue and I needed an algorithm using a config file. In This way you can use multiple fields define by a configuration file (simulate just by a List<String) config)
public static void test() {
// Associate your configName with your Comparator
Map<String, Comparator<DocumentDto>> map = new HashMap<>();
map.put("id", new IdSort());
map.put("createUser", new DocumentUserSort());
map.put("documentType", new DocumentTypeSort());
/**
In your config.yml file, you'll have something like
sortlist:
- documentType
- createUser
- id
*/
List<String> config = new ArrayList<>();
config.add("documentType");
config.add("createUser");
config.add("id");
List<Comparator<DocumentDto>> sorts = new ArrayList<>();
for (String comparator : config) {
sorts.add(map.get(comparator));
}
// Begin creation of the list
DocumentDto d1 = new DocumentDto();
d1.setDocumentType(new DocumentTypeDto());
d1.getDocumentType().setCode("A");
d1.setId(1);
d1.setCreateUser("Djory");
DocumentDto d2 = new DocumentDto();
d2.setDocumentType(new DocumentTypeDto());
d2.getDocumentType().setCode("A");
d2.setId(2);
d2.setCreateUser("Alex");
DocumentDto d3 = new DocumentDto();
d3.setDocumentType(new DocumentTypeDto());
d3.getDocumentType().setCode("A");
d3.setId(3);
d3.setCreateUser("Djory");
DocumentDto d4 = new DocumentDto();
d4.setDocumentType(new DocumentTypeDto());
d4.getDocumentType().setCode("A");
d4.setId(4);
d4.setCreateUser("Alex");
DocumentDto d5 = new DocumentDto();
d5.setDocumentType(new DocumentTypeDto());
d5.getDocumentType().setCode("D");
d5.setId(5);
d5.setCreateUser("Djory");
DocumentDto d6 = new DocumentDto();
d6.setDocumentType(new DocumentTypeDto());
d6.getDocumentType().setCode("B");
d6.setId(6);
d6.setCreateUser("Alex");
DocumentDto d7 = new DocumentDto();
d7.setDocumentType(new DocumentTypeDto());
d7.getDocumentType().setCode("B");
d7.setId(7);
d7.setCreateUser("Alex");
List<DocumentDto> documents = new ArrayList<>();
documents.add(d1);
documents.add(d2);
documents.add(d3);
documents.add(d4);
documents.add(d5);
documents.add(d6);
documents.add(d7);
// End creation of the list
// The Sort
Stream<DocumentDto> docStream = documents.stream();
// we need to reverse this list in order to sort by documentType first because stream are pull-based, last sorted() will have the priority
Collections.reverse(sorts);
for(Comparator<DocumentDto> entitySort : sorts){
docStream = docStream.sorted(entitySort);
}
documents = docStream.collect(Collectors.toList());
// documents has been sorted has you configured
// in case of equality second sort will be used.
System.out.println(documents);
}
Comparator objects are really simple.
public class IdSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getId().compareTo(o2.getId());
}
}
public class DocumentUserSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getCreateUser().compareTo(o2.getCreateUser());
}
}
public class DocumentTypeSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getDocumentType().getCode().compareTo(o2.getDocumentType().getCode());
}
}
Conclusion : this method isn't has efficient but you can create generic sort using a file configuration in this way.
Here is a full example comparing 2 fields in an object, one String and one int, also using Collator to sort.
public class Test {
public static void main(String[] args) {
Collator myCollator;
myCollator = Collator.getInstance(Locale.US);
List<Item> items = new ArrayList<Item>();
items.add(new Item("costrels", 1039737, ""));
items.add(new Item("Costs", 1570019, ""));
items.add(new Item("costs", 310831, ""));
items.add(new Item("costs", 310832, ""));
Collections.sort(items, new Comparator<Item>() {
#Override
public int compare(final Item record1, final Item record2) {
int c;
//c = record1.item1.compareTo(record2.item1); //optional comparison without Collator
c = myCollator.compare(record1.item1, record2.item1);
if (c == 0)
{
return record1.item2 < record2.item2 ? -1
: record1.item2 > record2.item2 ? 1
: 0;
}
return c;
}
});
for (Item item : items)
{
System.out.println(item.item1);
System.out.println(item.item2);
}
}
public static class Item
{
public String item1;
public int item2;
public String item3;
public Item(String item1, int item2, String item3)
{
this.item1 = item1;
this.item2 = item2;
this.item3 = item3;
}
}
}
Output:
costrels
1039737
costs
310831
costs
310832
Costs
1570019
A lot of answers above have fields compared in single comparator method which is not actually working. There are some answers though with different comparators implemented for each field, I am posting this because this example would be much more clearer and simple to understand I am believing.
class Student{
Integer bornYear;
Integer bornMonth;
Integer bornDay;
public Student(int bornYear, int bornMonth, int bornDay) {
this.bornYear = bornYear;
this.bornMonth = bornMonth;
this.bornDay = bornDay;
}
public Student(int bornYear, int bornMonth) {
this.bornYear = bornYear;
this.bornMonth = bornMonth;
}
public Student(int bornYear) {
this.bornYear = bornYear;
}
public Integer getBornYear() {
return bornYear;
}
public void setBornYear(int bornYear) {
this.bornYear = bornYear;
}
public Integer getBornMonth() {
return bornMonth;
}
public void setBornMonth(int bornMonth) {
this.bornMonth = bornMonth;
}
public Integer getBornDay() {
return bornDay;
}
public void setBornDay(int bornDay) {
this.bornDay = bornDay;
}
#Override
public String toString() {
return "Student [bornYear=" + bornYear + ", bornMonth=" + bornMonth + ", bornDay=" + bornDay + "]";
}
}
class TestClass
{
// Comparator problem in JAVA for sorting objects based on multiple fields
public static void main(String[] args)
{
int N,c;// Number of threads
Student s1=new Student(2018,12);
Student s2=new Student(2018,12);
Student s3=new Student(2018,11);
Student s4=new Student(2017,6);
Student s5=new Student(2017,4);
Student s6=new Student(2016,8);
Student s7=new Student(2018);
Student s8=new Student(2017,8);
Student s9=new Student(2017,2);
Student s10=new Student(2017,9);
List<Student> studentList=new ArrayList<>();
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
studentList.add(s5);
studentList.add(s6);
studentList.add(s7);
studentList.add(s8);
studentList.add(s9);
studentList.add(s10);
Comparator<Student> byMonth=new Comparator<Student>() {
#Override
public int compare(Student st1,Student st2) {
if(st1.getBornMonth()!=null && st2.getBornMonth()!=null) {
return st2.getBornMonth()-st1.getBornMonth();
}
else if(st1.getBornMonth()!=null) {
return 1;
}
else {
return -1;
}
}};
Collections.sort(studentList, new Comparator<Student>() {
#Override
public int compare(Student st1,Student st2) {
return st2.getBornYear()-st1.getBornYear();
}}.thenComparing(byMonth));
System.out.println("The sorted students list in descending is"+Arrays.deepToString(studentList.toArray()));
}
}
OUTPUT
The sorted students list in descending is[Student [bornYear=2018, bornMonth=null, bornDay=null], Student [bornYear=2018, bornMonth=12, bornDay=null], Student [bornYear=2018, bornMonth=12, bornDay=null], Student [bornYear=2018, bornMonth=11, bornDay=null], Student [bornYear=2017, bornMonth=9, bornDay=null], Student [bornYear=2017, bornMonth=8, bornDay=null], Student [bornYear=2017, bornMonth=6, bornDay=null], Student [bornYear=2017, bornMonth=4, bornDay=null], Student [bornYear=2017, bornMonth=2, bornDay=null], Student [bornYear=2016, bornMonth=8, bornDay=null]]
im my case List of Lists (in the approximation examle):
List<T>.steam
.map(Class1.StaticInnerClass1::Field1)
.flatMap(x -> x.getField11ListStaticInnerClass2OfField1.stream())
.max(Comparator.comparing(Class1.StaticInnerClass2::Field21,Collections.reverseOrder())
.thenCompare(Class1.StaticInnerClass2::Field22));
For my case, I had 3 fields (For example - int index, bool isArchive ,bool isClassPrivate)
and I summed their comparison result like this-
Collections.sort(getData(), (o1, o2) ->
Integer.compare(o1.getIndex(getContext()), o2.getIndex(getContext()))
+ Boolean.compare(o1.isArchive(), o2.isArchive())
+ Boolean.compare(o1.isClassPrivate(), o2.isClassPrivate()
));