I have some User objects that can have name and email, I want them sorted by name , then by email, but firstly show users that have not null names. I was trying like so:
Collections.sort(usersList, new Comparator<User>() {
#Override
public int compare(User obj1, User obj2) {
int res = 0;
//to make sure null names at the end
if (obj1.getFullName() == null && obj2.getFullName() == null)
res = 0;
if (obj1.getFullName() == null)
res = 1;
if (obj2.getFullName() == null)
res = -1;
//logic for name sorting then email sorting
if (obj1.getFullName() != null && obj2.getFullName() != null)
res = obj1.getFullName().compareTo(obj2.getFullName());
if (res == 0)
res = obj1.getEmail().compareTo(obj2.getEmail());
return res;
}
});
As result I get names first but sorted by email not by name.
This can be easily done with Java-8's Comparator utilities:
List<User> users = Arrays.asList(new User("pete", "pete#email"), new User(null, null), new User("alex", null), new User(null, "email"));
Collections.sort(users, Comparator.comparing(User::getName, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Comparator.comparing(User::getEmail, Comparator.nullsLast(Comparator.naturalOrder()))));
System.out.println(users);
Output:
[
User{name='alex', email='null'},
User{name='pete', email='pete#email'},
User{name='null', email='email'},
User{name='null', email='null'}
]
Related
I want to compare 2 objects and create a new one with the values of the second if its values are not null. Otherwise the values of the first one should be copied to the object created.
My problem is that I don't know how to set the value which I want to copy. I know that to get the name of the attribute I can use field.getName(), but how can I do the set of that attribute on the new object?
Example:
Test t1 = new Test();
t1.name = "Maria";
t1.age = 30;
t1.street = "somewhere";
Test t2 = new Test();
t2.name = "Maria";
t2.age = ;
t2.street = "here and here";
Test resultIwant = new Test();
t2.name = "Maria";
t2.age = 30;
t2.street = "here and here";
Code:
Test resultIwant = new Test(t2);
for(Field field : t1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value1= field.get(t1);
Object value2= field.get(t2);
if ((value2 == null && value1 == null) || (value2 == value1))
continue;
else {
if(value2 != null && value1 == null)
continue;
else if(value2 == null && value1 != null) {
resultIwant.set???? = value1; <----------- this is my problem
}
}
}
The Java Reflection API not only allows you to read but also to set a field value.
In your code you can try the following:
Test resultIwant = new Test(t2);
for(Field field : t1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value1= field.get(t1);
Object value2= field.get(t2);
if ((value2 == null && value1 == null) || (value2 == value1))
continue;
else {
if(value2 != null && value1 == null)
continue;
else if(value2 == null && value1 != null) {
String fieldName = field.getName();
Field fieldOnResultIwant =
resultIwant.getClass().getDeclaredField(fieldName);
fieldOnResultIwant.setAccessible(true);
// Honestly, I do not remember... Perhaps also will work:
// field.set(resultIwant, value1);
fieldOnResultIwant.set(resultIwant, value1);
}
}
}
I posted a more complete, but related, answer here.
So basically i have this java project for school, and it requires me to read information from a text file, create an object from that information, and then insert that object into a hash table.
I am able to read the information from the text file and create the object, however, for some reason, my program isnt inserting the object into the hash table. Because then at the end of the program, when i try to search for the object (via its ID), it just returns 'not found'.
EDIT: My question is not how to compare strings. It is how to take the information from the text file, turn it into an object, and then insert it into the hash table. I know how to take the info and turn it into an object, but for some reason, it is not being inserted into the hashtable. Nothing i am inserting into the hashtable from the textfile is actually being inserted. That is the problem I am having.
This is the code i have written:
package test;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class testmain {
public static void main(String[] args) throws FileNotFoundException{
StorageSystem ss = new StorageSystem();
ss.createhashtable();
String line;
String [] parts;
File in = new File("database.txt");
Scanner sc = new Scanner(in);
while(sc.hasNextLine()){
line = sc.nextLine();
parts = line.split(" ");
String type = parts[0];
String name = parts[1];
String id = parts[2];
String price = parts[3];
if(type.equals("Insert")){
Product p1 = new Product(name, id, Double.parseDouble(price));
ss.insert(p1);
}
else if(type.equals("remove")){
ss.remove(id);
}
}
sc.close();
System.out.println(ss.searchbyID("123"));
System.out.println(ss.searchbyID("232"));
System.out.println(ss.searchbyID("444"));
System.out.println(ss.searchbyID("456"));
//If i do it manually (below), it works. But thats not the point of this project
/*
Product ex = new Product("joe", "123", 22.33);
ss.insert(ex);
System.out.println(ss.searchbyID("123"));
ss.remove("123");
System.out.println(ss.searchbyID("123"));
*/
//When i do it this way ^^, it inserts the object into the hashtable, it
//searches for the object and finds it. it then removes the object. and
//when it searches again it doesnt find anything. And thats how it
//should work.
}
}
//Below is the class that contains the information on the hashtable
import java.io.*;
import java.util.Scanner;
public class StorageSystem {
private final static int tablesize = 1000;
private static Product[] table = new Product[tablesize];
public StorageSystem(){
table = new Product[tablesize];
for(int i = 0; i < tablesize; i++){
table[i] = null;
}
}
public void createhashtable(){
table = new Product[tablesize];
for(int i = 0; i < tablesize; i++){
table[i] = null;
}
}
public void useExistingTable(Product[] pt){
table = pt;
}
public String searchbyID(String idnum){
int hash = (Integer.parseInt(idnum) % tablesize);
if(table[hash] == null){
return "Not Found.";
}
else{
Product entry = table[hash];
while((entry != null) && (entry.getID() != idnum)){
entry = entry.getNext();
}
if(entry == null){
return "Not Found.";
}
else
return entry.toString();
}
}
public void insert(Product p){
String n = p.getName();
String i = p.getID();
double pr = p.getPrice();
int hash = (Integer.parseInt(i) % tablesize);
if(table[hash] == null){
table[hash] = new Product(n, i, pr);
}
else{
Product entry = table[hash];
while((entry.getNext() != null) && (entry.getID() != i)){
entry = entry.getNext();
}
entry.setNext(new Product(n, i, pr));
}
}
public void remove(String idnum) {
int hash = (Integer.parseInt(idnum) % tablesize);
if (table[hash] != null) {
Product prevEntry = null;
Product entry = table[hash];
while (entry.getNext() != null && entry.getID() != idnum) {
prevEntry = entry;
entry = entry.getNext();
}
if (entry.getID() == idnum) {
if (prevEntry == null)
table[hash] = entry.getNext();
else
prevEntry.setNext(entry.getNext());
}
}
}
}
If anyone could help me out with this problem that would be great. I have been looking at this thing for a few days now and cant get it to work.
entry.getID() != idnum
change to
!idnum.equals(entry.getID())
in case of entry.getID() returns null
entry.getID() == idnum
is also the same
==================
update:
Since you said the object is not insert into array. I guess it happens here:
There's an id let's say "100" in array. And you want to insert an Product with id "100", so it goes to
else{
Product entry = table[hash];
while((entry.getNext() != null) && (entry.getID() != i)){
entry = entry.getNext();
}
entry.setNext(new Product(n, i, pr));
}
You use entry.getID() != i or !i.equals(entry.getID(), both are ok. But in the end, you set new Product to entry.next.
But in your searchByID() method. You are trying to search through table[hash]
while((entry != null) && (entry.getID() != idnum)){
entry = entry.getNext(); // comment 1
}
if(entry == null){
return "Not Found.";
}
Issue happens here, entry is null will jump out the loop. It means when while loop is done, entry would always be null. You can set a breakpoint on comment 1.
I need to find best matched employee salary in the DB records as:
Name: City: State:
A (null) (null)
A (null) DEL
(null) (null) (null)
A SAKET DEL
Match order should be:
1. NAME = name, STATE = state, CITY = city
2. NAME = name, STATE = state , CITY = NULL
3. NAME = name, STATE = NULL, CITY = NULL
4. NAME = NULL, STATE = NULL, CITY = NULL
Means if in a row where all attributes matches – it should be selected, if we do not have that kind of data we should go to next best option like select state and city as NULL, etc.
My code as below, is giving me correct results but I need a more efficient way.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
BigDecimal salaryWithState = null;
BigDecimal salaryWithName = null;
BigDecimal salaryWithNoMatch = null;
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = null;
if(results.getString("SALARY") != null){
salaryRslt = BigDecimal.valueOf(results.getDouble("SALARY"));
}
if(billerName != null && !billerName.equals("") && billerName.equals(request.getBillPaymentsalaryCalculateInfo().getBillerName())){
if(city != null && !city.equals("") && city.equals(request.getMsgRqHdr().getCity()) &&
state != null && !state.equals("") && state.equalsIgnoreCase(request.getMsgRqHdr().getstate())){
salary = salaryRslt;
break;
} else if((city == null || city.equals("")) && state != null && !state.equals("") &&
state.equalsIgnoreCase(request.getMsgRqHdr().getState())){
salaryWithState = salaryRslt;
} else if((city == null || city.equals("")) && (state == null || state.equals(""))){
salaryWithName = salaryRslt;
}
} else if((billerName == null || billerName.equals("")) && (city == null || city.equals("")) &&
(state == null || state.equals(""))){
salaryWithNoMatch = salaryRslt;
}
}
if(salary != null){
return salary;
} else if(salaryWithState != null){
salary = salaryWithState;
} else if(salaryWithName != null){
salary = salaryWithName;
} else if(salaryWithNoMatch != null){
salary = salaryWithNoMatch;
}
return salary;
}
EDIT: I dont want to use 3 extra variables: salaryWithState, salaryWithName, salaryWithNoMatch.
I want just to give the general idea how this can be implemented, so I haven't actually tested and checked if it will give you the right salary.
public BigDecimal getSalaryForBestMatch(ResultSet resultSet, PaymentSalaryInfo paymentSalaryInfo) {
Map<String, Supplier<String>> m1 = new HashMap<>();
m1.put("EMP_NAME", paymentSalaryInfo::getBillerName);
m1.put("STATE", paymentSalaryInfo::getState);
m1.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m2 = new HashMap<>();
m2.put("STATE", paymentSalaryInfo::getState);
m2.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m3 = new HashMap<>();
m3.put("CITY", paymentSalaryInfo::getCity);
Optional<String> salary = Optional.empty();
while(resultSet.next() && !salary.isPresent()) {
salary = apply(m1, resultSet);
//check salary and then apply(m2, resultSet) ....
}
return salary.isPresent() ? new BigDecimal(salary.get()) : null;
}
public Optional<String> apply(Map<String, Supplier<String>> filter, ResultSet resultSet) {
boolean allMatch = filter.entrySet().stream().allMatch(entry -> {
String value = resultSet.getString(entry.getKey());
return value != null && value.equals(entry.getValue().get());
});
return allMatch ? Optional.of(resultSet.getString("salary")) : Optional.empty();
}
I have written the same logic in a different way with using arrays. If your environment can afford to use arrays, you can use this code. But I have not tested the code.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
int matchCount = 0;
String rBillerName = request.getBillPaymentsalaryCalculateInfo().getBillerName();
String rCity = request.getMsgRqHdr().getCity();
String rState = request.getMsgRqHdr().getstate();
String [] truthArray = new String[] {rBillerName, rCity, rState};
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = results.getString("SALARY") != null ? BigDecimal.valueOf(results.getDouble("SALARY")): null;
String [] testArray = new String[] {billerName, city, state};
int localMatchCount = 0;
for(int i = 0; i < testArray.length; i++) {
if(testArray[i] != null && testArray[i].equals(truthArray[i]))
localMatchCount++;
else {
break;
}
}
if(localMatchCount >= matchCount){
matchCount = localMatchCount;
salary = salaryRslt;
}
}
return salary;
}
I have a HashMap of employees:
Employee{
String name;
String id;
String Salary;
}
Map<String,Employee> emps = new HashMap<>();
emps.put("1",employee1);
emps.put("2",employee2);
emps.put("3",employee3);
I want have following scenarios:
All employees have name ==> Pass
All employess dont have name(name=null) ==> Pass
3. Other cases must throw an exception.
Example: employee2 does not have a name, but employee1 and employee3 do.
How can I write such scenario?
You can use Streams to filter employees that have or don't have a name, count them and compare the result to the size of the list.
long count = emps.values()
.stream()
.filter(employee -> employee.getName() != null)
.count();
/**
* count == 0 => All employess dont have name
* count == size => All employees have name
*/
return count == 0 || count == employees.size();
You can iterate over the values of the map by calling (map.values()) ,which give you Collection .Then apply your logic.
Collection<Employee> values = emps.values();
int count = 0;
for (Employee employee : values) {
if(null != employee.name){
count ++;
}
}
return count == 0 || count == emps.size();
1) Get list of employees
Collection<Employee> employees = employeesMap.values();
testEmployees(employees);
The program will stop with an exception if employees do not pass the test. This is how to test them
public void testEmployees(List<Employee> employees) {
int nullNameEmployeeCount = 0;
int size = employes.size();
for (Employee employee : employees) {
if (employee.getName() == null) {
nullNameEmployeeCount++;
}
}
if (nullNameEmployeeCount == 0 || nullNameEmployeeCount == size) {
System.out.println("All employees name are not nulls or nulls");
} else {
throw new EmployeesNotPassException();
}
}
And
public class EmployeesNotPassException extends RuntimeException {}
public class Pass {
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
boolean res = true;
for (Employee e : list) {
res = res && isPass(e);
}
System.out.println(res);
}
public static boolean isPass(Employee employee) {
if (employee == null)
return false;
if (employee.getName() == null)
return true;
if (employee.getName() != null && !StringUtils.isEmpty(employee.getName())) {
return true;
}
return false;
}
}
I have a class Contact with fields firstName, lastName and emails. I need to sort them using Collection.sort(...), but I got an exception:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
My compareTo method:
#Override
public int compareTo(Contact another) {
int compareFirstName = 0;
if (this.getFirstName() != null && another.getFirstName() != null) {
compareFirstName = this.getFirstName().compareToIgnoreCase(
another.getFirstName());
if (compareFirstName == 0) {
int compareLastName = 0;
if (this.getLastName() != null && another.getLastName() != null) {
compareLastName = this.getLastName().compareToIgnoreCase(
another.getLastName());
if (compareLastName == 0) {
int compareEmail = 0;
if (this.getEmails() != null
&& another.getEmails() != null) {
compareEmail = this.getEmails()
.compareToIgnoreCase(another.getEmails());
return compareEmail;
} else {
return 0;
}
} else {
return compareLastName;
}
} else {
int compareEmail = 0;
if (this.getEmails() != null && another.getEmails() != null) {
compareEmail = this.getEmails().compareToIgnoreCase(
another.getEmails());
return compareEmail;
} else {
return 0;
}
}
} else {
return compareFirstName;
}
} else {
int compareLastName = 0;
if (this.getLastName() != null && another.getLastName() != null) {
compareLastName = this.getLastName().compareToIgnoreCase(
another.getLastName());
if (compareLastName == 0) {
int compareEmail = 0;
if (this.getEmails() != null && another.getEmails() != null) {
compareEmail = this.getEmails().compareToIgnoreCase(
another.getEmails());
return compareEmail;
} else {
return 0;
}
} else {
return compareLastName;
}
} else {
int compareEmail = 0;
if (this.getEmails() != null && another.getEmails() != null) {
compareEmail = this.getEmails().compareToIgnoreCase(
another.getEmails());
return compareEmail;
} else {
return 0;
}
}
}
}
Please help me to find error in my compareTo method. Thanks.
Your implementation does violate the contract.
Suppose you have 3 Contacts :
contact1 : First Name = "John", Last Name = "Doe", Email = "x#gmail.com"
contact2 : First Name = "John", Last Name = "Doe", Email = null
contact3 : First Name = "John", Last Name = null, Email = "y#gmail.com"
Based on your logic :
contact1.compareTo(contact2) returns 0 (since they have the same first and last name).
contact2.compareTo(contact3) also returns 0 (since you only compare by first name).
But contact1.compareTo(contact3) doesn't return 0 (since they have different emails).
compareTo must be transitive.
The way to fix this is not to ignore a property that is null only in one of the contacts you are comparing. For example, if this.getLastName()==null && another.getLastName() != null, return 1 (assuming you want to order the null last names after the non-null last names).