I have the following problem to solve. I am using Java.
A restaurant recognizes 3 types of customers: “NEWBIES”, “REGULARS” and “VIPs”. When customers place their orders, all the orders join a queue. However the orders are always served in such a way that VIPs are served before regulars who are served before newbies.
I need a class which could be used to sort the customer orders. In case two customers are of the same type, the orderID should be used to sort them.
How can I sort by order priority based on the customer type using comparator?
Assuming I already have the following class Order
public class Order
{
public static int orderID;
private int tableNumber;
private String[] orderDetails;
private String customerType;
public Order(int tableNumber, String[] orderDetails, String customerType)
{
this.tableNumber = tableNumber;
this.orderDetails = orderDetails;
this.customerType = customerType;
orderID += 1;
}
// get and set methods declared
}
I have implemented the comparator as follows:
import java.util.Comparator;
public class OrderComparator implements Comparator<Order>
{
#Override
public int compare(Order o1, Order o2)
{
if(o1.getType().equals(o2.getType()))
return o1.getOrderID - o2.getOrderID;
else
// How does comparing the customer type text ensure that
// it will be printed in the right order?
return o1.getType().compareTo(o2.getType());
}
}
Not only do you want to sort on multiple fields, you also want a custom sort with one of those fields.
In the code below, I filled in the missing parts of both class Order and class OrderComparator. Notes after the code.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Order {
public static final String NEWBIES = "NEWBIES";
public static final String REGULARS = "REGULARS";
public static final String VIP = "VIP";
private static int orderId;
private int orderID;
private int tableNumber;
private String[] orderDetails;
private String customerType;
public Order(int tableNumber, String[] orderDetails, String customerType) {
this.tableNumber = tableNumber;
this.orderDetails = orderDetails;
this.customerType = customerType;
orderID = ++orderId;
}
public int getOrderID() {
return orderID;
}
public int getTableNumber() {
return tableNumber;
}
public String[] getOrderDetails() {
return orderDetails;
}
public String getType() {
return customerType;
}
public String toString() {
return String.format("%d %s", orderID, customerType);
}
public static void main(String[] args) {
Order order1 = new Order(0, null, VIP);
Order order2 = new Order(0, null, REGULARS);
Order order3 = new Order(0, null, REGULARS);
List<Order> list = new ArrayList<>();
list.add(order3);
list.add(order2);
list.add(order1);
System.out.println("Unordered: " + list);
Collections.sort(list, new OrderComparator());
System.out.println("Ordered: " + list);
}
}
class OrderComparator implements Comparator<Order> {
#Override
public int compare(Order o1, Order o2) {
if (o1.getType().equals(o2.getType())) {
return o1.getOrderID() - o2.getOrderID();
}
else {
if (Order.VIP.equals(o1.getType())) {
return -1;
}
else if (Order.VIP.equals(o2.getType())) {
return 1;
}
else if (Order.REGULARS.equals(o1.getType())) {
return -1;
}
else if (Order.REGULARS.equals(o2.getType())) {
return 1;
}
else if (Order.NEWBIES.equals(o1.getType())) {
return -1;
}
else if (Order.NEWBIES.equals(o2.getType())) {
return 1;
}
throw new RuntimeException("Unexpected customer type.");
}
}
}
I added method main to class Order in order to test the code.
I added method toString to class Order so as to be able to check whether the code produces the expected results.
I understand that you want a kind of numerator for Order objects. Hence I made member orderID an instance member since every Order has its own ID and I added a new static member orderId (note that Java is case sensitive) which produces a new, unique order ID for each new Order object.
You want VIP orders to come before REGULARS orders and you want REGULARS orders to come before NEWBIES orders. By default, a Comparator sorts by ascending order, hence you want VIP to be lowest and NEWBIES to be highest (purely for sorting purposes). So in method compare (of class OrderComparator), if, for example, the type of o1 is VIP and the type of o2 is REGULARS then you want VIP to be lower that REGULAR. Hence in that situation, method compare returns -1 (minus one).
Running the above code produces the following output.
Unordered: [3 REGULARS, 2 REGULARS, 1 VIP]
Ordered: [1 VIP, 2 REGULARS, 3 REGULARS]
Note that since customerType (in class Order) is a String, there is a chance that an Order object will be created with an invalid customerType value. You could change the constructor of class Order and add a check for the supplied value (for customerType) and throw an Exception if the supplied value is invalid. Or you could use enum (also known as enumerated types). The below code uses enum instead of String for customerType - which also simplifies method compare in class OrderComparator.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Order {
private static int orderId;
private int orderID;
private int tableNumber;
private String[] orderDetails;
private CustomerType customerType;
public Order(int tableNumber, String[] orderDetails, CustomerType customerType) {
this.tableNumber = tableNumber;
this.orderDetails = orderDetails;
this.customerType = customerType;
orderID = ++orderId;
}
public int getOrderID() {
return orderID;
}
public int getTableNumber() {
return tableNumber;
}
public String[] getOrderDetails() {
return orderDetails;
}
public CustomerType getType() {
return customerType;
}
public String toString() {
return String.format("%d %s", orderID, customerType);
}
public static void main(String[] args) {
Order order1 = new Order(0, null, CustomerType.VIP);
Order order2 = new Order(0, null, CustomerType.REGULARS);
Order order3 = new Order(0, null, CustomerType.REGULARS);
List<Order> list = new ArrayList<>();
list.add(order3);
list.add(order2);
list.add(order1);
System.out.println("Unordered: " + list);
Collections.sort(list, new OrderComparator());
System.out.println("Ordered: " + list);
}
}
class OrderComparator implements Comparator<Order> {
#Override
public int compare(Order o1, Order o2) {
if (o1.getType().equals(o2.getType())) {
return o1.getOrderID() - o2.getOrderID();
}
else {
return o2.getType().ordinal() - o1.getType().ordinal();
}
}
}
enum CustomerType {
NEWBIES, REGULARS, VIP
}
You can read this question How to sort a collection by multiple fields. Especially the second answer, first option listed.
My application contains a TextField and a ListView. The TextField allows users to enter search terms that will filter the contents of the ListView as they type.
The filtering process will match several fields within each DataItem in the ListView and return the results if any of them match.
What I want to do, however, is have those results prioritize items that match one particular field over the others.
For example, in the MCVE below, I have two items: Computer and Paper. The Computer item has a keyword for "paper," so searching for "paper" should return Computer as a result.
However, since I also have an item called Paper, the search should return Paper at the top of the list. In the MCVE, though, the results are still alphabetized:
Question: How would I go about ensuring any matches to the DataItem.name are listed above matches to a DataItem.keywords?
EDIT: Entering "pap" in the search field should also return "Paper" at the top, followed by the remaining matches, as the partial search term partially matches the DataItem name.
MCVE
DataItem.java:
import java.util.List;
public class DataItem {
// Instance properties
private final IntegerProperty id = new SimpleIntegerProperty();
private final StringProperty name = new SimpleStringProperty();
private final StringProperty description = new SimpleStringProperty();
// List of search keywords
private final ObjectProperty<List<String>> keywords = new SimpleObjectProperty<>();
public DataItem(int id, String name, String description, List<String> keywords) {
this.id.set(id);
this.name.set(name);
this.description.set(description);
this.keywords.set(keywords);
}
/**
* Creates a space-separated String of all the keywords; used for filtering later
*/
public String getKeywordsString() {
StringBuilder sb = new StringBuilder();
for (String keyword : keywords.get()) {
sb.append(keyword).append(" ");
}
return sb.toString();
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public String getDescription() {
return description.get();
}
public StringProperty descriptionProperty() {
return description;
}
public List<String> getKeywords() {
return keywords.get();
}
public ObjectProperty<List<String>> keywordsProperty() {
return keywords;
}
#Override
public String toString() {
return name.get();
}
}
Main.java:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main extends Application {
// TextField used for filtering the ListView
TextField txtSearch = new TextField();
// ListView to hold our DataItems
ListView<DataItem> dataItemListView = new ListView<>();
// The ObservableList of DataItems
ObservableList<DataItem> dataItems;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Add the search field and ListView to the layout
root.getChildren().addAll(txtSearch, dataItemListView);
// Build the dataItems List
dataItems = FXCollections.observableArrayList(buildDataItems());
// Add the filter logic
addSearchFilter();
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
/**
* Adds the functionality to filter the list dynamically as search terms are entered
*/
private void addSearchFilter() {
// Wrap the dataItems list in a filtered list, initially showing all items, alphabetically
FilteredList<DataItem> filteredList = new FilteredList<>(
dataItems.sorted(Comparator.comparing(DataItem::getName)));
// Add the predicate to filter the list whenever the search field changes
txtSearch.textProperty().addListener((observable, oldValue, newValue) ->
filteredList.setPredicate(dataItem -> {
// Clear any selection already present
dataItemListView.getSelectionModel().clearSelection();
// If the search field is empty, show all DataItems
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Compare the DataItem's name and keywords with the search query (ignoring case)
String query = newValue.toLowerCase();
if (dataItem.getName().toLowerCase().contains(query)) {
// DataItem's name contains the search query
return true;
} else {
// Otherwise check if any of the search terms match those in the DataItem's keywords
// We split the query by space so we can match DataItems with multiple keywords
String[] searchTerms = query.split(" ");
boolean match = false;
for (String searchTerm : searchTerms) {
match = dataItem.getKeywordsString().toLowerCase().contains(searchTerm);
}
return match;
}
}));
// Wrap the filtered list in a SortedList
SortedList<DataItem> sortedList = new SortedList<>(filteredList);
// Update the ListView
dataItemListView.setItems(sortedList);
}
/**
* Generates a list of sample products
*/
private List<DataItem> buildDataItems() {
List<DataItem> dataItems = new ArrayList<>();
dataItems.add(new DataItem(
1, "School Supplies", "Learn things.",
Arrays.asList("pens", "pencils", "paper", "eraser")));
dataItems.add(new DataItem(
2, "Computer", "Do some things",
Arrays.asList("paper", "cpu", "keyboard", "monitor")));
dataItems.add(new DataItem(
3, "Keyboard", "Type things",
Arrays.asList("keys", "numpad", "input")));
dataItems.add(new DataItem(
4, "Printer", "Print things.",
Arrays.asList("paper", "ink", "computer")));
dataItems.add(new DataItem(
5, "Paper", "Report things.",
Arrays.asList("write", "printer", "notebook")));
return dataItems;
}
}
If not mistaken you only need to find a way to sort your filtered results correctly. To keep it simple I will use this comparator instead of yours :
Comparator<DataItem> byName = new Comparator<DataItem>() {
#Override
public int compare(DataItem o1, DataItem o2) {
String searchKey = txtSearch.getText().toLowerCase();
int item1Score = findScore(o1.getName().toLowerCase(), searchKey);
int item2Score = findScore(o2.getName().toLowerCase(), searchKey);
if (item1Score > item2Score) {
return -1;
}
if (item2Score > item1Score) {
return 1;
}
return 0;
}
private int findScore(String itemName, String searchKey) {
int sum = 0;
if (itemName.startsWith(searchKey)) {
sum += 2;
}
if (itemName.contains(searchKey)) {
sum += 1;
}
return sum;
}
};
In the code above, I compare two DataItem. Each one will have a 'score' which depends on how similar their names are from our search keyword. For simplicity lets say we give 1 point if the searchKey appeared in the name of our item and 2 points if the item name starts with the searchKey, so now we can compare those two and sort them. If we return -1 the item1 will be placed first, if we return 1 then the item2 will be placed first and return 0 otherwise.
Here is addSearchFilter() method I used in your example :
private void addSearchFilter() {
FilteredList<DataItem> filteredList = new FilteredList<>(dataItems);
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> filteredList.setPredicate(dataItem -> {
dataItemListView.getSelectionModel().clearSelection();
if (newValue == null || newValue.isEmpty()) {
return true;
}
String query = newValue.toLowerCase();
if (dataItem.getName().toLowerCase().contains(query)) {
return true;
} else {
String[] searchTerms = query.split(" ");
boolean match = false;
for (String searchTerm : searchTerms) {
match = dataItem.getKeywordsString().toLowerCase().contains(searchTerm);
}
return match;
}
}));
SortedList<DataItem> sortedList = new SortedList<>(filteredList);
Comparator<DataItem> byName = new Comparator<DataItem>() {
#Override
public int compare(DataItem o1, DataItem o2) {
String searchKey = txtSearch.getText().toLowerCase();
int item1Score = findScore(o1.getName().toLowerCase(), searchKey);
int item2Score = findScore(o2.getName().toLowerCase(), searchKey);
if (item1Score > item2Score) {
return -1;
}
if (item2Score > item1Score) {
return 1;
}
return 0;
}
private int findScore(String itemName, String searchKey) {
int sum = 0;
if (itemName.startsWith(searchKey)) {
sum += 2;
}
if (itemName.contains(searchKey)) {
sum += 1;
}
return sum;
}
};
sortedList.setComparator(byName);
dataItemListView.setItems(sortedList);
}
Of course the findScore() could be more sophisticated if you want to create a more complex score system (for example checking upper and lower case letters, give more points depending the position of the keyword found in the item name etc).
I may have found a different way to accomplish this. Instead of using a Predicate, I've changed the ChangeListener to just use a couple of loops and build a new List manually:
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.isEmpty()) {
// Reset the ListView to show all items
dataItemListView.setItems(dataItems);
return;
}
ObservableList<DataItem> filteredList = FXCollections.observableArrayList();
String query = newValue.toLowerCase().trim();
// First, look for exact matches within the DataItem's name
for (DataItem item : dataItems) {
if (item.getName().toLowerCase().contains(query)) {
filteredList.add(0, item);
} else {
// If the item's name doesn't match, we'll look through search terms instead
String[] searchTerms = query.split(" ");
for (String searchTerm : searchTerms) {
// If the item has this searchTerm and has not already been added to the filteredList, add it
// now
if (item.getKeywordsString().toLowerCase().contains(searchTerm)
&& !filteredList.contains(item)) {
filteredList.add(item);
}
}
}
}
dataItemListView.setItems(filteredList);
I'll leave the question unanswered for now to see if anyone has a solution for using the Predicate as well.
This question already has answers here:
Why do I get an UnsupportedOperationException when trying to remove an element from a List?
(17 answers)
Closed 6 years ago.
Initial Starting Point
I have an existing List of 1000 Person objects which I would like to insert an Extractor to listen for property changes within any Person objects (this ObservableList will be later attached to a TableView).
So my code would be like:
ObservableList<Person> observablePersons = FXCollections.observableList(personlist,
personextractor);
Error Message
But when I try to add a new person objects to this ObservableList observablePersons, I run into this error:
run:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at com.sun.javafx.collections.ObservableListWrapper.doAdd(ObservableListWrapper.java:101)
at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:151)
at java.util.AbstractList.add(AbstractList.java:108)
at test.listchangelistener.listChangeDemo.main(listChangeDemo.java:72)
Java Result: 1
Could you please tell me why would I come across this error message? My java version is jdk1.8.0_91 (32-Bit)
Person Class
package test.listchangelistener;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
class Person {
private final IntegerProperty age = new SimpleIntegerProperty();
private final StringProperty name = new SimpleStringProperty();
public Person(String name, Integer age) {
setName(name);
setAge(age);
}
public int getAge() {
return age.get();
}
public final void setAge(int value) {
age.set(value);
}
public IntegerProperty ageProperty() {
return age;
}
public String getName() {
return name.get();
}
public final void setName(String value) {
name.set(value);
}
public StringProperty nameProperty() {
return name;
}
#Override
public String toString() {
return "Person{" + "age=" + age.get() + ", name=" + name.get() + '}';
}
}
Test Code
package test.listchangelistener;
import java.util.Arrays;
import java.util.List;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.util.Callback;
public class listChangeDemo {
public static void main(String[] args) {
Person p1 = new Person("Ted", 26);
Person p2 = new Person("Anne", 19);
// just a simple list
List<Person> persons = Arrays.asList(p1, p2);
// extractor to observe change of person properties
Callback<Person, Observable[]> extractor = (Person p) -> {
return new Observable[]{
p.ageProperty(),
p.nameProperty()
};
};
// make list observable and attach extractor
ObservableList<Person> observablePersons = FXCollections.observableList(persons, extractor);
// create listchangeListener for observableList
ListChangeListener listener = (ListChangeListener) (ListChangeListener.Change c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println("these were added: ");
List addedSubList = c.getAddedSubList();
addedSubList.forEach((Object t) -> {
System.out.println("added Person: " + t);
});
} else if (c.wasRemoved()) {
System.out.println("these were removed");
List removedSubList = c.getRemoved();
removedSubList.forEach((Object t) -> {
System.out.println("removed Person: " + t);
});
} else if (c.wasUpdated()) {
System.out.println("these were updated");
System.out.println("Updated elements are: "
+ c.getList().subList(c.getFrom(), c.getTo()));
}
}
};
// attach listchangeListener to observableList
observablePersons.addListener(listener);
// testing changes
observablePersons.add(new Person("Siegfried", 10));
}
}
The problem is coming from the creation of your backing List:
List<Person> persons = Arrays.asList(p1, p2);
If you take a look on the javadoc of Arrays.asList:
Returns a fixed-size list backed by the specified array.
On a closer look you got an UnsupportedOperationException because List.add() is an optional operation:
Throws:
UnsupportedOperationException - if the add operation is not supported by this list.
You can update the creation of the backing list as:
List<Person> persons = new ArrayList<Person>(Arrays.asList(p1, p2));
The difference is that the returned ArrayList is an exact, independent copy of the passed one, therefore the restriction described above is not valid anymore in this case.
You can also take a look on this question which explaines this topic in depths:
Difference between Arrays.asList(array) vs new ArrayList<Integer>(Arrays.asList(ia)) in java
I have simple class
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
}
and List<ActiveAlarm> con. How to sort in ascending order by timeStarted, then by timeEnded? Can anybody help? I know in C++ with generic algorithm and overload operator <, but I am new to Java.
Using Comparator
For Example:
class Score {
private String name;
private List<Integer> scores;
// +accessor methods
}
Collections.sort(scores, new Comparator<Score>() {
public int compare(Score o1, Score o2) {
// compare two instance of `Score` and return `int` as result.
return o2.getScores().get(0).compareTo(o1.getScores().get(0));
}
});
With Java 8 onwards, you can simply use lambda expression to represent Comparator instance.
Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Either make ActiveAlarm implement Comparable<ActiveAlarm> or implement Comparator<ActiveAlarm> in a separate class. Then call:
Collections.sort(list);
or
Collections.sort(list, comparator);
In general, it's a good idea to implement Comparable<T> if there's a single "natural" sort order... otherwise (if you happen to want to sort in a particular order, but might equally easily want a different one) it's better to implement Comparator<T>. This particular situation could go either way, to be honest... but I'd probably stick with the more flexible Comparator<T> option.
EDIT: Sample implementation:
public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
#Override
public int compare(ActiveAlarm x, ActiveAlarm y) {
// TODO: Handle null x or y values
int startComparison = compare(x.timeStarted, y.timeStarted);
return startComparison != 0 ? startComparison
: compare(x.timeEnded, y.timeEnded);
}
// I don't know why this isn't in Long...
private static int compare(long a, long b) {
return a < b ? -1
: a > b ? 1
: 0;
}
}
JAVA 8 and Above Answer (Using Lambda Expressions)
In Java 8, Lambda expressions were introduced to make this even easier! Instead of creating a Comparator() object with all of it's scaffolding, you can simplify it as follows: (Using your object as an example)
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);
or even shorter:
Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));
That one statement is equivalent to the following:
Collections.sort(list, new Comparator<ActiveAlarm>() {
#Override
public int compare(ActiveAlarm a1, ActiveAlarm a2) {
return a1.timeStarted - a2.timeStarted;
}
});
Think of Lambda expressions as only requiring you to put in the relevant parts of the code: the method signature and what gets returned.
Another part of your question was how to compare against multiple fields. To do that with Lambda expressions, you can use the .thenComparing() function to effectively combine two comparisons into one:
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted
.thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);
The above code will sort the list first by timeStarted, and then by timeEnded (for those records that have the same timeStarted).
One last note: It is easy to compare 'long' or 'int' primitives, you can just subtract one from the other. If you are comparing objects ('Long' or 'String'), I suggest you use their built-in comparison. Example:
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );
EDIT: Thanks to Lukas Eder for pointing me to .thenComparing() function.
We can sort the list in one of two ways:
1. Using Comparator : When required to use the sort logic in multiple places
If you want to use the sorting logic in a single place, then you can write an anonymous inner class as follows, or else extract the comparator and use it in multiple places
Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
public int compare(ActiveAlarm o1, ActiveAlarm o2) {
//Sorts by 'TimeStarted' property
return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
}
//If 'TimeStarted' property is equal sorts by 'TimeEnded' property
public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
}
});
We can have null check for the properties, if we could have used 'Long' instead of 'long'.
2. Using Comparable(natural ordering): If sort algorithm always stick to one property:
write a class that implements 'Comparable' and override 'compareTo' method as defined below
class ActiveAlarm implements Comparable<ActiveAlarm>{
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public ActiveAlarm(long timeStarted,long timeEnded) {
this.timeStarted=timeStarted;
this.timeEnded=timeEnded;
}
public long getTimeStarted() {
return timeStarted;
}
public long getTimeEnded() {
return timeEnded;
}
public int compareTo(ActiveAlarm o) {
return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}
public int doSecodaryOrderSort(ActiveAlarm o) {
return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}
}
call sort method to sort based on natural ordering
Collections.sort(list);
In java8+ this can be written in single line as follows:
collectionObjec.sort(comparator_lamda) or comparator.comparing(CollectionType::getterOfProperty)
code:
ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
or
ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
public class ActiveAlarm implements Comparable<ActiveAlarm> {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public int compareTo(ActiveAlarm a) {
if ( this.timeStarted > a.timeStarted )
return 1;
else if ( this.timeStarted < a.timeStarted )
return -1;
else {
if ( this.timeEnded > a.timeEnded )
return 1;
else
return -1;
}
}
That should give you a rough idea. Once that's done, you can call Collections.sort() on the list.
Since Java8 this can be done even cleaner using a combination of Comparator and Lambda expressions
For Example:
class Student{
private String name;
private List<Score> scores;
// +accessor methods
}
class Score {
private int grade;
// +accessor methods
}
Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);
Java-8 solution using Stream API:
A. When timeStarted and timeEnded are public (as mentioned in the requirement) and therefore do not (need to) have public getter methods:
List<ActiveAlarm> sorted =
list.stream()
.sorted(Comparator.comparingLong((ActiveAlarm alarm) -> alarm.timeStarted)
.thenComparingLong((ActiveAlarm alarm) -> alarm.timeEnded))
.collect(Collectors.toList());
B. When timeStarted and timeEnded have public getter methods:
List<ActiveAlarm> sorted =
list.stream()
.sorted(Comparator.comparingLong(ActiveAlarm::getTimeStarted)
.thenComparingLong(ActiveAlarm::getTimeEnded))
.collect(Collectors.toList());
If you want to sort the original list itself:
A. When timeStarted and timeEnded are public (as mentioned in the requirement) and therefore do not (need to) have public getter methods:
list.sort(Comparator.comparingLong((ActiveAlarm alarm) -> alarm.timeStarted)
.thenComparingLong((ActiveAlarm alarm) -> alarm.timeEnded));
B. When timeStarted and timeEnded have public getter methods:
list.sort(Comparator.comparingLong(ActiveAlarm::getTimeStarted)
.thenComparingLong(ActiveAlarm::getTimeEnded));
Guava's ComparisonChain:
Collections.sort(list, new Comparator<ActiveAlarm>(){
#Override
public int compare(ActiveAlarm a1, ActiveAlarm a2) {
return ComparisonChain.start()
.compare(a1.timestarted, a2.timestarted)
//...
.compare(a1.timeEnded, a1.timeEnded).result();
}});
We can use the Comparator.comparing() method to sort a list based on an object's property.
class SortTest{
public static void main(String[] args) {
ArrayList<ActiveAlarm> activeAlarms = new ArrayList<>(){{
add(new ActiveAlarm("Alarm 1", 5, 10));
add(new ActiveAlarm("Alarm 2", 2, 12));
add(new ActiveAlarm("Alarm 3", 0, 8));
}};
/* I sort the arraylist here using the getter methods */
activeAlarms.sort(Comparator.comparing(ActiveAlarm::getTimeStarted)
.thenComparing(ActiveAlarm::getTimeEnded));
System.out.println(activeAlarms);
}
}
Note that before doing it, you'll have to define at least the getter methods of the properties you want to base your sort on.
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public ActiveAlarm(String name, long timeStarted, long timeEnded) {
this.name = name;
this.timeStarted = timeStarted;
this.timeEnded = timeEnded;
}
public long getTimeStarted() {
return timeStarted;
}
public long getTimeEnded() {
return timeEnded;
}
#Override
public String toString() {
return name;
}
}
Output:
[Alarm 3, Alarm 2, Alarm 1]
Employee POJO Class
package in.ac.adit.oop.sort;
public class Employee {
private int id;
private String name;
private String department;
public int getId() {
return id;
}
public Employee() {
super();
}
public Employee(int id, String name, String department) {
super();
this.id = id;
this.name = name;
this.department = department;
}
#Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", department=" + department + "]";
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Employee Class To Manage Employee
package in.ac.adit.oop.sort;
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
/*
* Create 10 Employee Object
*/
Employee emp1 = new Employee(1, "Nayan", "IT");
Employee emp2 = new Employee(2, "Siddarth", "CP");
Employee emp3 = new Employee(3, "Samarth", "AE");
Employee emp4 = new Employee(4, "Bhavesh", "CV");
Employee emp5 = new Employee(5, "Sam", "FT");
Employee emp6 = new Employee(6, "Keyur", "IT");
Employee emp7 = new Employee(7, "Bala", "ME");
Employee emp8 = new Employee(8, "Mitul", "ME");
Employee emp9 = new Employee(9, "Kamlesh", "EE");
Employee emp10 = new Employee(10, "Piyush", "EE");
/*
* List of Employee Object
*/
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);
employeeList.add(emp4);
employeeList.add(emp5);
employeeList.add(emp6);
employeeList.add(emp7);
employeeList.add(emp8);
employeeList.add(emp9);
employeeList.add(emp10);
CustomObjectSort customObjectSort = new CustomObjectSort();
List<Employee> sortByDepartment = customObjectSort.sortByDepartment(employeeList);
/*
* Sorted By Department
*/
for (Employee employee : sortByDepartment) {
System.out.println(employee);
}
/*
* Sorted By Name
*/
List<Employee> sortByName = customObjectSort.sortByName(employeeList);
for (Employee employee : sortByName) {
System.out.println(employee);
}
/*
* Sorted By Id
*/
List<Employee> sortById = customObjectSort.sortById(employeeList);
for (Employee employee : sortById) {
System.out.println(employee);
}
}
}
Custom Sorting
package in.ac.adit.oop.sort;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CustomObjectSort {
public List<Employee> sortByName(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getName().compareTo(employee2.getName());
}
});
return employeeList;
}
public List<Employee> sortByDepartment(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getDepartment().compareTo(employee2.getDepartment());
}
});
return employeeList;
}
public List<Employee> sortById(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getId() - employee2.getId();
}
});
return employeeList;
}
}
You can use Collections.sort and pass your own Comparator<ActiveAlarm>
In java you need to use the static Collections.sort method. Here is an example for a list of CompanyRole objects, sorted first by begin and then by end. You can easily adapt for your own object.
private static void order(List<TextComponent> roles) {
Collections.sort(roles, new Comparator() {
#Override
public int compare(Object o1, Object o2) {
int x1 = ((CompanyRole) o1).getBegin();
int x2 = ((CompanyRole) o2).getBegin();
if (x1 != x2) {
return x1 - x2;
} else {
int y1 = ((CompanyRole) o1).getEnd();
int y2 = ((CompanyRole) o2).getEnd();
return y2 - y1;
}
}
});
}
You can call Collections.sort() and pass in a Comparator which you need to write to compare different properties of the object.
As mentioned you can sort by:
Making your object implement Comparable
Or pass a Comparator to Collections.sort
If you do both, the Comparable will be ignored and Comparator will be used. This helps that the value objects has their own logical Comparable which is most reasonable sort for your value object, while each individual use case has its own implementation.
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
}
List<ActiveAlarm> con = new ArrayList<ActiveAlarm>();
Collections.sort(con , (a1, a2) -> a1.timeStarted.compareTo(a2.timeStarted));
Collections.sort(con , (a1, a2) -> a1.timeEnded.compareTo(a2.timeEnded));
Here's what did the trick for me.
Was much shorter and easier than everything else I found:
Collections.sort(listName, Comparator.comparing(Object::getProperty).reversed());
The ".reversed()" part at the end was a requirement for my specific project but I'm sharing it too, as it took a while to find it
The best and the easiest way to sort any list of objects in Java (Java 8 and above).
Lets sort a basket of fruits based on the property "fruitName"
Fruit POJO:
class Fruit
{
int price;
String fruitName;
public Fruit(int price, String fruitName) {
super();
this.price = price;
this.fruitName = fruitName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
#Override
public String toString() {
return "Fruits [price=" + price + ", fruitName=" + fruitName + "]";
}
}
Now lets add fruits into a list and then sort it
List<Fruit> basketOfFruits = new ArrayList<>();
basketOfFruits.add(new Fruit(123, "oranges"));
basketOfFruits.add(new Fruit(45, "nectarine"));
basketOfFruits.add(new Fruit(369, "blueberries"));
basketOfFruits.add(new Fruit(248, "apple"));
basketOfFruits.add(new Fruit(968, "peaches"));
basketOfFruits.add(new Fruit(436, "grapes"));
basketOfFruits.add(new Fruit(596, "figs"));
//sorting by the property fruitName
Collections.sort(basketOfFruits, (f1, f2)->{return f1.getFruitName().compareTo(f2.getFruitName());});
You can now print the list (i.e basketOfFruits) and the fruits in the list would be sorted in ASCENDING order (lexicographically).
The output would look like this:
[Fruits [price=248, fruitName=apple], Fruits [price=369, fruitName=blueberries], Fruits [price=596, fruitName=figs], Fruits [price=436, fruitName=grapes], Fruits [price=45, fruitName=nectarine], Fruits [price=123, fruitName=oranges], Fruits [price=968, fruitName=peaches]]
Instead of Collections.sort(), Java streams can also be used (Java 8 and above). The following is the code using Java streams
List<Fruit> sortedFruits = basketOfFruits.stream().sorted( (f1, f2)->{return f1.getFruitName().compareTo(f2.getFruitName());}).collect(Collectors.toList());
here the list is sorted in the same manner as Collections.sort(), but the sorted items would be stored/collected in another list "sortedFruits". So, if we want to print the sorted items of the list, we need to print "sortedFruits" instead of "basketOfFruits" in this case
I am working on a java/Javafx project for the first time and i have a TableView with multiple column (name, prename, age...) to present my data and I need the user to be able to select a single row and give me everytime all anformation about the person(Other columns) even when he click at another column but I haven't been able to find the right way to do it.
When i select a row my code give everytime the value of the cell i click on, but i need other informations to search with in my SQLite data base and work on it (Delete/edit this person..)
Here is the code that i use:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observableValue, Object oldValue, Object newValue) {
//Check whether item is selected and set value of selected item to Label
if (tbl_elev.getSelectionModel().getSelectedItem() != null) {
TableViewSelectionModel<Student> selectionModel = tbl_elev.getSelectionModel();
ObservableList<?> selectedCells = selectionModel.getSelectedCells();
#SuppressWarnings("unchecked")
TablePosition<Object, ?> tablePosition = (TablePosition<Object, ?>) selectedCells.get(0);
Object val = tablePosition.getTableColumn().getCellData(newValue);
System.out.println("Selected Value " + val);
}
}
});
}
... //rest of code
I am waiting for your suggestions and ideas, i dont mind if you suggest another approach because this may be uncompatible (taken from internet) Please if you need any other part of the code just comment, i don't put it all because it is too long to read.. (Sorry of my bad english)
If you specify that the ChangeListener parameters are of type Student you can get use the instance methods from that object:
Here's a Minimal, Complete, and Verifiable example:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SSCCE extends Application {
#Override
public void start(Stage stage) {
VBox root = new VBox();
TableView<Student> studentsTable = new TableView<Student>();
HBox studentBox = new HBox();
Label studentHeader = new Label("Student: ");
Label studentInfo = new Label("");
studentBox.getChildren().addAll(studentHeader, studentInfo);
root.getChildren().addAll(studentsTable, studentBox);
// Prepare the columns
TableColumn<Student, String> firstNameCol = new TableColumn<Student, String>(
"First name");
firstNameCol.setCellValueFactory(cellData -> cellData.getValue()
.firstNameProperty());
TableColumn<Student, String> lastNameCol = new TableColumn<Student, String>(
"Last name");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue()
.lastNameProperty());
studentsTable.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(
ObservableValue<? extends Student> observable,
Student oldValue, Student newValue ) {
studentInfo.setText(newValue.getFirstName() + " "
+ newValue.getLastName());
// If you want to get the value of a selected student cell at
// anytime, even if it hasn't changed. Just do e.g.
// studentsTable.getSelectionModel().getSelectedItem().getFirstName()
}
});
studentsTable.getColumns().setAll(firstNameCol, lastNameCol);
// Some mock Student objects
Student student1 = new Student("Eric", "Smith");
Student student2 = new Student("Brad", "Jones");
Student student3 = new Student("Logan", "Thorpe");
// Fill the table with students.
studentsTable.getItems().addAll(student1, student2, student3);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
// The student class. In this case an inner class to simplify the example. But generally you should never use inner classes.
class Student {
private StringProperty firstName;
private StringProperty lastName;
public Student(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
}
}
After too many failed attempts and thanks to #Jonatan 's answer the code after i compelete some missing words should be like this:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(ObservableValue<? extends Student> observable,Student oldValue, Student newValue){
if(newValue!=null){
System.out.println(newValue.getName() + " "+ newValue.getPrename()+" "+newValue.getNaiss());
}
//you can add any other value from Student class via getter(getAdr,getMail,...)
}
});
}
... //rest of code
Output example:
Jonatan stenbacka 2015-09-11
Those value are ready for use to fetch the data base and specify the needed row in it to work on.
Hope that this help someone one day.
thanks...