I have developed a pojo named Employee.java. Now I was planning to make it as user defined collection. I want to make a map and store all the employee type objects in it.
Below is my pojo
public class Employee {
String name,job;
int salary;
public Employee(String n , String j, int t ) //constructor
{
this.name= n;
this.job=j;
this.salary= t;
}
#Override
public int hashCode()
{
return name.hashCode()+job.hashCode()+salary;
}
#Override
public boolean equals(Object obj) {
Employee e = (Employee) obj;
return this.name.equals(e.name)&&this.job.equals(e.job)&&this.salary==e.salary;
}
}
Now I have developed another class that contains map and will store employee type objects..
public static void main(String[] args)
{
Map employeeMap = new HashMap();
Employee e = new Employee("Saral", "Trainer", 34000);
Employee e1 = new Employee("Sarall", "saral", 34090);
employeeMap.put("S", e);
employeeMap.put("S1", e);
System.out.println(employeeMap.size());
Set s = employeeMap.entrySet();
Iterator it = s.iterator();
while(it.hasNext())
{
Map.Entry m =(Map.Entry)it.next();
System.out.println(m.getKey()+"\t"+m.getValue());
}
but when I try to run it , I want to fetch the employee details but I GET DISPLAYED THE OBJECT ON SCREEN ...I want to see the employees value, Please advise me how to get values from employee object.
2
S CollectionsPrac.Employee#285c2854
S1 CollectionsPrac.Employee#285c2854
You need to override the toString method in your Employee class, for example:
public String toString() {
return name + " [" + job + "] - salary: " + salary;
}
By the way, you can replace:
Iterator it = s.iterator();
while(it.hasNext())
{
Map.Entry m =(Map.Entry)it.next();
System.out.println(m.getKey()+"\t"+m.getValue());
}
with
System.out.println(s.toString());
Unless you really want the output to be tab separated.
You need to override the toString() method of Employee
#Override pulic String toString() {
return name + " " + job;
}
First of all. Your hashcode is broken.
Try running this:
System.out.println("Should be false: " + (new Employee("Sara", "Trainer", 1).hashCode() == new Employee("Trainer", "Sara", 1).hashCode()));
If you are using and IDE (like eclipse) there is a function to generate equals and hashcode methods automatically and you would get something like this:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((job == null) ? 0 : job.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + salary;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (job == null) {
if (other.job != null)
return false;
} else if (!job.equals(other.job))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (salary != other.salary)
return false;
return true;
}
As for your main method.. You should try to learn some basics about generics (the stuff inside the <>). You don't need the nity grity details at first. Just learn how to use it with lists and maps.. It will make your life a lot easier. Especially since your using and IDE...
Here is a refactored version of your main method:
public static void main(String[] args)
{
Map<String, Employee> employeeMap = new HashMap<String, Employee>();
Employee e = new Employee("Saral", "Trainer", 34000);
Employee e1 = new Employee("Sarall", "saral", 34090);
employeeMap.put("S", e);
employeeMap.put("S1", e1);
System.out.println(employeeMap.size());
Set<Entry<String, Employee>> entrySet = employeeMap.entrySet();
for (Entry<String, Employee> entry: entrySet) {
System.out.println(entry.getKey()+"\t"+entry.getValue().name);
}
System.out.println("Should be false: " + (new Employee("Sara", "Trainer", 1).hashCode() == new Employee("Trainer", "Sara", 1).hashCode()));
}
Change this in
Iterator it = s.iterator();
while(it.hasNext())
{
Map.Entry m =(Map.Entry)it.next();
Employee empl = (Employee) m.getValue();
System.out.println(m.getKey()+"\t"+empl.name);
}
As you can see with the line
Employee empl = (Employee) m.getValue();
the value is "casted" to an Employee object, and you can start to work with empl variable and use all the Employee class methods and members.
Related
I have a class say Student that have a field marks of type Double. I am creating a list of Student objects. Each student object may have marks field set to null or set with same value in different student objects. I have a problem where I want to return a single student object from this list based on below conditions:
when all students have same marks then return null.
else return student that have highest marks.
I wonder if there is a better approach using java stream api to get it done. Thank you in advance.
Student class:
public class Student {
private Double marks;
public Double getMarks() {
return marks;
}
public void setMarks(Double marks) {
this.marks = marks;
}
#Override
public String toString() {
return "Student [marks=" + marks + "]";
}
public Student(Double marks) {
super();
this.marks = marks;
}
}
Using Streams, you can do it while collecting to TreeMap and verifying the lastEntry as in:
private Student highestMarkUniqueStudent(List<Student> studentList) {
if(studentList.size() == 0) return null;
if(studentList.size() == 1) return studentList.get(0);
TreeMap<Integer, List<Student>> map = new TreeMap<>(studentList.stream()
.collect(Collectors.groupingBy(Student::getMarks)));
List<Student> highestMarkStudents = map.lastEntry().getValue();
// only one highest or all same marks or more than one with highest mark
return highestMarkStudents.size() == 1 ? highestMarkStudents.get(0) : null;
}
You can do like this:
Indeed by using TreeMap you have the highest marks in the first entry. so by checking its value, you can return desire result. if all marks have the same value so the first entry has to value more than one and if you have two or more highest marks then the first entry has still more than one student object in the list.
TreeMap<Integer, List<Student>> map = list.stream()
.collect(Collectors.groupingBy(
Student::getMarks,
() -> new TreeMap<Integer, List<Student>>(Comparator.reverseOrder()),
Collectors.mapping(Function.identity(), Collectors.toList())));
Map.Entry<Integer, List<Student>> firstEntry = map.firstEntry();
if (firstEntry.getValue().size() <= 1) {
result = firstEntry.getValue().get(0);
}
You can work with streams, but you'd need a helper object which would be able to collect your data.
The work now either can be done with redution or with collection.
With reduction, you'd do
students.stream().reduce(
new Helper(),
(helper, student) -> new Helper(helper, student));
class Helper {
private Student bestStudent = null;
private boolean different = false;
public Helper() {
}
public Helper(Helper oldHelper, Student newStudent) {
if (oldHelper.bestStudent == null) {
bestStudent = newStudent;
} else if (student.getMark() > oldHelper.bestStudent.getMark()) {
different = true;
bestStudent = student;
} else if (student.getMark() < oldHelper.bestStudent.getMark()) {
different = true;
}
}
public Student getResult() {
return different ? bestStudent : null;
}
}
but that creates a new Helper object for every Student.
With collection, we'd do
students.stream().collect(Helper::new, Helper::accept, Helper::combine);
class Helper {
private Student bestStudent = null;
private boolean different = false;
public Helper() {
}
public void accept(Student newStudent) {
if (bestStudent == null) {
bestStudent = newStudent;
} else if (newStudent.getMark() > bestStudent.getMark()) {
different = true;
bestStudent = newStudent;
} else if (newStudent.getMark() < bestStudent.getMark()) {
different = true;
}
}
public void combine() (Helper other) {
if (bestStudent == null) {
bestStudent = other.bestStudent;
different = other.different;
} else if (other.bestStudent != null && other.bestStudent.getMark() > bestStudent.getMark()) {
different = true;
bestStudent = other.bestStudent;
} else if (other.bestStudent != null && other.bestStudent.getMark() < bestStudent.getMark()) {
different = true;
}
}
public Student getResult() {
return different ? bestStudent : null;
}
}
(Note: 1. Code not tested, 2. parts of the basic logic taken from another answer.)
You might not need streams. Sort the list highest mark firsts, if the first 2 are same, return null, otherwise return the first student.
students.sort(Comparator.comparing(Student::getMarks).reversed());
boolean firstTwoHaveSameMarks = students.get(0).getMarks().equals(students.get(1).getMarks());
return firstTwoHaveSameMarks ? null : students.get(0);
If 2 or more students have the same highest mark, it returns null, otherwise it returns the student.
This question already has answers here:
Java 8, Streams to find the duplicate elements
(17 answers)
Closed 3 years ago.
I have an employee class with id, name and address fields. Two employees are considered the same if their id and name are exactly same. Now I have a list of employees, now my task is to get the collection of duplicate employees.
Here is my code for Employee class with hascode and equals methods overriden based on id and name fields.
class Employee {
int id;
String name;
String address;
public Employee(int id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
#Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", address=" + address + "]";
}
// auto generated by eclipse based on fields for id and name
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Now I have this code to find the duplicate employees
public static void main(String[] args) {
Employee e1 = new Employee(1, "John", "SFO");
Employee e2 = new Employee(2, "Doe", "NY");
Employee e3 = new Employee(1, "John", "NJ");
List<Employee> list = Arrays.asList(e1, e2, e3);
Set<Employee> set = new HashSet<>();
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) {
set.add(list.get(i));
}
}
}
System.out.println(set);
}
This code works fine and gives me employee with id 1 in my set.
How to do the same operation using Java 8 lamda's and streams? Is flatmap is helpful in this case?
Your requirement if kinda specific and not really helpful in most cases. I would do something like that instead:
final Map<Employee, Long> groupedWithCount = employees.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Now you have all the info you need, and more. Employees are grouped by count in this map, for your data it looks like that:
{
Employee [id=2, name=Doe, address=NY] = 1,
Employee [id=1, name=John, address=SFO] = 2
}
Obviously, duplicates are entries with value > 1.
Another approach:
list.stream()
.collect(groupingBy(identity(), counting()))
.entrySet()
.stream()
.filter(e -> e.getValue() != 1)
.map(Map.Entry::getKey)
.collect(toList());
or :
list.stream()
.collect(groupingBy(identity()))
.values()
.stream()
.filter(l -> l.size() != 1)
.map(l -> l.get(0)) // The list cannot be empty
.collect(toList());
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 flat data that represent the hierarchical relationship as below:
ID Name PID
0 A NULL
1 B 0
2 C 0
4 D 1
5 E 1
6 F 4
3 G 0
This table represents the 'data table', where PID indicates the parent element.
For example, in the first row we see that A has PID null whereas B has PID 0, which means that B’s parent is A, because 0 is the ID of A, and A is the root element, because it does not have a PID. Similarly, C has parent A because C too has PID 0, and 0 is the ID of A.
I create a class RecordHolder to represent the above table. I also implement the method processRecordHolder
public Map<String, List<String>> processRecordHolder()
The returned map uses element as keys, and holds collections of descendant nodes as values. For example, the first item in the map corresponds to element A, which has many descendants, whereas element C has no descendant. The order of members in the output is not important.
public static void main(String[] args) {
RecordHolder dt = new RecordHolder();
dt.addRow(0, "A", null);
dt.addRow(1, "B", 0);
dt.addRow(2, "C", 0);
dt.addRow(4, "D", 1);
dt.addRow(5, "E", 1);
dt.addRow(6, "F", 4);
dt.addRow(3, "G", 0);
System.out.println("Output:");
System.out.println(dt.processRecordHolder());
}
Output:
{D=[F], A=[B, C, G, D, E, F], B=[D, E, F]}
or
{D=[F], E=null, F=null, G=null, A=[B, C, G, D, E, F], B=[D, E, F], C=null}
Below is my implementation of Record which I am able to come up so far:
public class Record {
public Integer id;
public String name;
public Integer parentId;
public Record parent;
public Collection<Record> children;
public Record(Integer id, String name, Integer parentId) {
this();
this.id = id;
this.name = name;
this.parentId = parentId;
}
public Record() {
children = Collections.newSetFromMap(new ConcurrentHashMap<Record, Boolean>())
}
public Collection<Record> getChildren() {
return children;
}
public Record getParent() {
return parent;
}
public Integer getParentId() {
return parentId;
}
#Override
public String toString() {
return "Record{" + "id=" + id + ", name=" + name + ", parentId=" + parentId + '}';
}
/* (non-Javadoc)
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((parentId == null) ? 0 : parentId.hashCode());
return result;
}
/* (non-Javadoc)
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Record)) {
return false;
}
Record other = (Record) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (parentId == null) {
if (other.parentId != null) {
return false;
}
} else if (!parentId.equals(other.parentId)) {
return false;
}
return true;
}
}
Now I am not able to understand other step what should I do?
Try:
public class RecordHolder {
Map<Integer,String> namesById = new HashMap<>();
Map<Integer,List<Integer>> childrenById = new HashMap<>();
public void addRow(Integer id, String name, Integer parent) {
namesById.put(id, name);
List<Integer> children = childrenById.get(parent);
if (children == null) {
children = new ArrayList<>();
childrenById.put(parent, children);
}
children.add(id);
}
public Map<String,List<String>> processRecordHolder() {
Map<String,List<String>> results = new HashMap<>();
descendants(null, results);
return results;
}
private List<String> descendants(Integer id, Map<String, List<String>> results) {
final List<String> childrenNames = new ArrayList<>();
final List<Integer> childrenIds = childrenById.get(id);
if (childrenIds != null && childrenIds.size() > 0) {
for (Integer childrenId : childrenIds) {
final String childName = namesById.get(childrenId);
childrenNames.add(childName);
final List<String> grandchildrenNames = descendants(childrenId, results);
childrenNames.addAll(grandchildrenNames);
}
if (id != null) {
results.put(namesById.get(id), childrenNames);
}
}
return childrenNames;
}
}
Just in case you'd like to try my simpler idea for implementation, here it is in brief detail. This way, you can decide whether you want to use your current idea or try to start over using this idea. (note the code below is presented as a pseudo-Java outline, it won't compile and is not tested):
int numNodes = 7;
Node[] nodes = new Node[numNodes];
//Read in your file here using a Scanner/FileReader or something
int ID = 0;
char value = 0;
int PID = 0;
while(scanner.hasNextLine()){
ID = scan.next();
value = scan.next();
PID = scan.next();
nodes[ID] = new Node(value, PID);
}
And then a node class:
class Node{
char value;
Node parent;
public Node(value, parentID){
this.value = value;
if(parentID == -1)
parent = null;
else
parent = nodes[parentID]; //nodes will have to be a global array or get passed to the constructor
}
}
Note that this constructor will only work if the item in nodes[parentID] has been initialized previously. (This is the case for your current input file order, but might not be in other situations.)
Ancestry:
To find the ancestry of a node using ID with this approach simply do:
printAncestry(nodes[ID]);
void printAncestry(Node n){
System.out.println("Child: " + n.value);
System.out.println("Ancestry: ");
while(n.parent != null){
n = n.parent;
System.out.println(n.value);
}
}
I have defined a simple private class named SetOb which contains an int and a Set data structure. I have a HashMap in the 'main' method with SetOb as Key and Integer as value. Now as you can see in the main method, when I feed the HashMap with a SetOb instance and then look for an instance with exactly the same value, it returns 'null'. This has happened with me quite a few times before when I use my own defined data structures like SetOb as Key in HashMap. Can someone please point me what am I missing ?
Please note that in the constructor of SetOb class, I copy the Set passed as argument.
public class Solution {
public static Solution sample = new Solution();
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
}
public static void main(String[] args) {
Map<SetOb, Integer> m = new HashMap< SetOb, Integer>();
Set<Integer> a = new HashSet<Integer>();
for(int i =0; i<10; i++){
a.add(i);
}
SetOb x = sample.new SetOb(100, a);
SetOb y = sample.new SetOb(100, a);
m.put(x,500);
Integer val = m.get(y);
if(val!= null) System.out.println("Success: " + val);
else System.out.println("Failure");
}
}
Your x and y are not the same object instances hence contains is not able to match y against x, which ends up not finding the matching key/value in the Map.
If you want the match to succeed, please implement(override) hasCode & equals method in SetOb which will compare the field values.
Sample methods(Eclipse generated) as below:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + last;
result = prime * result + ((st == null) ? 0 : st.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetOb other = (SetOb) obj;
if (last != other.last)
return false;
if (st == null) {
if (other.st != null)
return false;
} else if (!st.equals(other.st))
return false;
return true;
}
The default implementation of hashCode uses object identity to determine the hash code. You will need to implement hashCode (and equals) in your private class if you want value identity. For instance:
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
#Override
public boolean equals(Object other) {
if (other.class == SetOb.class) {
SetOb otherSetOb = (SetOb) other;
return otherSetOb.last == last && otherSetOb.st.equals(st);
}
return false;
}
#Override
public int hashCode() {
return 37 * last + st.hashCode();
}
}
SetOb needs to override the hashCode() and thus the equals() methods.
Hash-based collections use these methods to store (hashCode()) and retrieve (hashCode()) and equals()) your objects.