This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed last month.
Given the following task. We have an Employee and a Company classes. Each instance of Employee class is stored in array Employee[] employees in the Company class. I need a method which removes an instance of Employee in the array Employee[] employees by id.
I managed to write the following code:
public class Employee {
protected final int id;
protected String name;
public Employee(int id, String name) {
this.id = id;
this.name= name;
}
public int getId() {
return id;
}
}
public class Company {
private Employee[] employees;
private int size;
private static final int defaultCapacity = 5;
public Company() {
this(defaultCapacity);
}
public Company(int capacity) {
if (capacity <= 0)
throw new RuntimeException("capacity is required");
employees = new Employee[capacity];
}
public Employee removeEmployee(int id) {
Collection<Employee> employeeList = Arrays.asList(employees)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
Employee[] employeeArray = employeeList.toArray(Employee[]::new);
for (int i = 0; i < size; i++) {
if(employeeArray[i].getId() == id) {
Employee removedEmployee = employees[i];
employeeList.remove(employeeArray[i]);
employees = employeeList
.stream()
.filter(Objects::nonNull)
.toArray(Employee[]::new);
return removedEmployee;
}
}
return null;
}
}
The problem is that my method public Employee removeEmployee(int id) throws NullPointerException if an element for removal is not found.
Question:
How can I rewrite the method public Employee removeEmployee(int id) using, for instance, Streams API and Optional in oder to get rid of NullPointerException in the method public Employee removeEmployee(int id)?
N.B.: The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
There is a lot of ways to get rid of the NullPointerException here.
If you want to keep using the stream API, you may want to use filter and findAny.
For example, you could modify the method to the following:
public Employee removeEmployee(int id) {
Optional<Employee> employee = Arrays.stream(employees)
.filter(Objects::nonNull)
.filter(x -> x.getId() == id).
.findAny();
if(employee.isEmpty())
return null;
employees = Arrays.stream(employees).filter(x -> x != employee.get()).toArray(Employee[]::new);
return employee.get();
}
However, I would highly advise using a List or even a Map instead of an Array for employees as this makes things way easier and faster:
public Employee removeEmployee(int id){
Optional<Employee> toRemove = employees.stream().filter(x -> x.getId() == id).findAny();
if(toRemove.isEmpty())
return null;
employees.remove(toRemove.get());
return toRemove.get();
}
Or not to use the Stream API:
public Employee removeEmployee(int id){
int idx;
for(idx = 0; idx < employees.length; idx++){
if(employees[idx] != null && employees[idx].getId() == id)
break;
}
if(idx == employees.length)
return null;
Employee value = employees[idx];
Employee[] newArr = new Employee[employees.length - 1];
// the parameters here are left as an exercise to the reader :P
System.arraycopy(newArr, ...);
System.arraycopy(newArr, ...);
employees = newArr;
return value;
}
The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
Streams doesn't buy you a lot in this case.
What you're supposed to do is to find the element with the target id, and if such an element exists, allocate a new array in memory with a length smaller by 1 copy all the elements apart from the one that was found, and assign employees with the reference to the new array.
To reduce the length, we can make use of the System.arraycopy(). First copy the elements before the target, and then after the target.
That's how it would look like with a plain index-based for-loop.
public Employee removeEmployee(int id) {
Employee result = null;
int index = -1;
for (int i = 0; i < employees.length; i++) {
if (employees[i] != null && employees[i].getId() == id) {
result = employees[i];
employees[i] = null;
break;
}
}
if (result != null) {
reduceLength(index);
}
return result;
}
public void reduceLength(int i) {
Employee[] newEmployees = new Employee[employees.length - 1];
System.arraycopy(employees, 0, newEmployees, 0, i);
System.arraycopy(employees, i + 1, newEmployees, i, employees.length - (i + 1));
employees = newEmployees;
}
If you want to do weird stuff and use Stream API and Optional at all costs, here how it can be done (but I would recommend to stick with the code above):
public Optional<Employee> removeEmployee(int id) {
Optional<Integer> index = IntStream.range(0, employees.length)
.filter(i -> employees[i] != null)
.filter(i -> employees[i].getId() == id)
.boxed() // otherwise will get OptionalInt which lacks map() method
.findFirst();
Optional<Employee> result = index.map(i -> employees[i]);
index.ifPresent(this::reduceLength);
return result;
}
Considering it's homework and constraints mentioned, i believe you are supposed to do all the work using the array only.
I'll provide some guideline and leave the actual implementation to you:
public class Company {
private Employee[] employees;
private int size;
public Employee removeEmployee(int id) {
int index = -1;
//find the index of employee with required id, you have mostly done that
if (index == -1) {
return null;
}
//save found employee to variable
//remove from array
//shift array to the left
//do not forget to use and reassign size variable where appropriate
}
//some extra
public void addEmployee(Employee employee) {
//resize array if necessary
//add employee at correct position in array
//do not forget to use and reassign size variable where appropriate
}
}
If you get stuck, you can look at the ArrayList class, your task is basically a simplified version of it. I strongly advise you to use this as source of inspiration only and not to copy the source code!!!
Related
I have 2 Lists:
// old list
List<Employee> oldList = new ArrayList<>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
Does anyone know how can I merge those 2 Lists to one List containing all the employees from both lists grouped by "PersonalNumber" and keeping the order of the elemets in newList?
newList comes from the Database with a predefined sorting, and I need to keep it that way, so I can't sort it again on the Java side
Result should be:
[
{"123", "new_name1", NEW},
{"123", "old_name1", OLD},
{"456", "new_name2", NEW},
{"456", "old_name1", OLD},
]
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers. I just want to "inject" each old employee under the new employee with the same personalNumber
You can do like this: As you mentioned that both lists have the same PersonalNumber so you can group by using this property. To ensure order based on the personalNumber, I've used LinkedHashMap.
Stream.concat(newList.stream(), oldList.stream())
.collect(Collectors.groupingBy(Employee::getPersonalNumber,
LinkedHashMap::new, Collectors.toList()))
.values().stream().flatMap(List::stream)
.collect(Collectors.toList());
Note: The result of stream#concat is ordered if both of the input streams are ordered.
Collections to sort should work for this.
newList.addAll(oldList);
Collections.sort(newList, Comparator.comparing(Employee::getPersonalNumber) );
The key is that, "This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort."
Since cannot sort the new list, I take that to mean you don't know the order of the new list. You can do it the ol' N^2 method.
for(int i = 0; i<newList.size(); i+=2){
String newNum = newList.get(i).getPersonalNumber();
Employee old = oldList.stream().filter(
emp->newNum.equals(
emp.getPersonalNumber()
)
).findFirst().orElse(null);
newList.add(i+1, old);
oldList.remove(old); //not nescessary?
}
Here is another approach with Java streams and InsStream
private List<Employee> mergeLists(List<Employee> oldList, List<Employee> newList) {
return IntStream.range(0, oldList.size())
.mapToObj(index -> Arrays.asList(newList.get(index), oldList.get(index)))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
This approach is possible because of
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers.
You can do it as follows:
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
Demo:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
enum Status {
OLD, NEW;
}
class Employee {
private String name;
private String personalNumber;
private Status status;
public Employee() {
super();
}
public Employee(String name, String personalNumber, Status status) {
this.name = name;
this.personalNumber = personalNumber;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPersonalNumber() {
return personalNumber;
}
public void setPersonalNumber(String personalNumber) {
this.personalNumber = personalNumber;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
#Override
public String toString() {
return personalNumber + ", " + name + ", " + status;
}
}
public class Main {
public static void main(String[] args) {
// old list
List<Employee> oldList = new ArrayList<Employee>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
// Display the result
result.forEach(System.out::println);
}
}
Output:
123, new_name1, NEW
123, old_name1, OLD
456, new_name2, NEW
456, old_name2, OLD
For Java practice, I am trying to create a method inside my EmployeesDirectory Class that:
Removes Duplicate entries from the array
The array should be the same length after removing duplicates
Non-Empty entries should be making a contiguous sequence at the beginning of the array - and the actualNum should keep a record of the entries
Duplicate Means: Same Name, Position and Salary
Here is my Current Code:
I am unsure on how to implement this - any help would be appreciated
class EmployeeDirectory {
private Employee dir[];
private int size;
private int actualNum;
public EmployeeDirectory(int n) {
this.size = n;
dir = new Employee[size];
}
public boolean add(String name, String position, double salary) {
if (dir[size-1] != null) {
dir[actualNum] = new Employee(name, position, salary);
actualNum++;
return true;
} else {
return false;
}
}
}
I'd rather you did not write a distinct method for removing duplicates. If I were you, I would search for duplicates in add method and then instantly decide whether I need to add Employee.
Also, why don't you use Sets (link for HashSet) instead of arrays for your purpose? Sets by their own definition disallow adding duplicates, so they seem to be appropriate as a solution
First of all, Override equals and hashCode methods in Employee class as follow
#Override
public boolean equals(Object other) {
if(this == other) return true;
if(other == null || (this.getClass() != other.getClass())){
return false;
}
Employee guest = (Employee) other;
return Objects.equals(guest.name, name)
&& Objects.equals(guest.position, position)
&& Objects.equals(guest.salary, salary);
}
#Override
public int hashCode() {
return Arrays.hashCode(new Object[] {
name,
position,
salary
});
}
Then you can use Stream API distinct method to remove duplicates
Returns a stream consisting of the distinct elements (according to
Object.equals(Object)) of this stream.
You can do it like so
Employee e1 = new Employee("John", "developer", 2000);
Employee e2 = new Employee("John", "developer", 2000);
Employee e3 = new Employee("Fres", "designer", 1500);
Employee[] allEmployees = new Employee[100];
allEmployees[0] = e1;
allEmployees[1] = e2;
allEmployees[2] = e3;
allEmployees = Arrays.asList(allEmployees).stream().distinct()
.toArray(Employee[]::new);
Arrays.asList(allEmployees).forEach(System.out::println);
Output: (keeping both empty and non-empty entries)
John developer 2000.0
Fres designer 1500.0
null
Unfortunately, I have not got the Employee class to verify my code, but try this:
void removeDuplicates() {
int length = dir.length;
HashSet set = new HashSet(Arrays.asList(dir));
dir = new Employee[length];
Employee[] temp = (Employee[]) set.toArray();
for (int index = 0; index < temp.length; index++)
dir[index] = temp[index];
}
The code must remain the size of array after deletion the duplicates. At the beginning of array there must be valid Employees, at the end - nulls.
And don't forget to add this at the beginning of your .java file
import java.util.Arrays;
import java.util.HashSet;
If your task states as "remove duplicates from array" (i. e. you cannot use ArrayList or control when adding items), you can use the following approach:
public void removeDuplicates() {
Set<Employee> d = new HashSet<>(); // here to store distinct items
int shift = 0;
for (int i = 0; i > dir.length; i++) {
if (d.contains(dir[i])) { // duplicate, shift += 1
shift++;
} else { // distinct
d.add(dir[i]); // copy to `d` set
dir[i - shift] = dir[i]; // move item left
}
}
for (int i = d.size(); i < dir.length; i++)
dir[i] = null; // fill rest of array with nulls
actualNum = d.size();
}
Here, shift variable stores number of duplicates found in the array so far. Every distinct item is moved to shift positions left in order to make sequence continuous while keeping initial ordering. Then remaining items are altered to nulls.
To make hash-based collections work with Employee instances correctly, you also need to override hashCode() and equals() methods as follows:
public class Employee {
//...
#Override
public int hashCode() {
return Objects.hash(name, position, salary);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!o.getType().equals(this.getType()) return false;
Employee e = (Employee) o;
return Objects.equals(e.name, name)
&& Objects.equals(e.position, position)
&& Objects.equals(e.salary, salary); // or e.salary == salary, if it primitive type
}
}
First of all I apologize for my english and this is my first time asking on stackoverflow so if i miss something please point it out.
So I'm new to java and trying out binary search with the help from my friend. The code is to display product information once searched with product ID. I manage to make it return the index number where Id is found but the problem is when i put in multiple same ID it only show 1 data. I want my program to show all the index where the ID-12 is found.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MyListBinarySearch {
public static void main(String a[]){
List<Emp> empList = new ArrayList<Emp>();
empList.add(new Emp(12,"Apple,50,10-5-2014"));
empList.add(new Emp(12,"Apple,50,5-5-2014"));
empList.add(new Emp(124,"Apple,50,2-5-2014"));
empList.add(new Emp(302,"Apple,50,2-5-2014"));
empList.add(new Emp(12,"Apple,50,2-5-2014"));
Emp searchKey = new Emp(12,"String");
int index = Collections.binarySearch(empList, searchKey, new EmpComp());
System.out.println("Index of the searched key: "+index);
}
}
class EmpComp implements Comparator<Emp>{
public int compare(Emp e1, Emp e2) {
if(e1.getEmpId() == e2.getEmpId()){
return 0;
} else {
return -1;
}
}
}
class Emp {
private int empId;
private String empInfo;
public Emp(int id, String info){
this.empId = id;
this.empInfo = info;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpInfo() {
return empInfo;
}
public void setEmpInfo(String empInfo) {
this.empInfo = empInfo;
}
#Override
public String toString(){
return empId+" : "+empInfo;
}
}
The out is "Index of searched key: 2"
I want to display all the index where the search key is found.
How do i do that ? Do i need to loop ?
You have two problems:
Your comparator should return something greater than 0 when the current element is greater than the compared element, 0 when the elements are equals and less than 0 when the current element is less than the compared element. Your current implementation doesn't cover this.
Binary search works on sorted arrays/lists only. Your list is not sorted by id.
After fixing this issues, then you will effectively use binary search. After retrieving the index where the element is 12, you can search around the element to retrieve all the elements that have the same Id.
This is an idea how to implement it:
int index = Collections.binarySearch(empList, searchKey, new EmpComp());
List<Emp> empsWithId12 = new ArrayList<Emp>();
for (int i = index - 1; i >= 0; i--) {
Emp emp = empList.get(i);
if (emp.getId() == 12) {
empsWithId12.add(emp);
} else {
break;
}
}
Collections.reverse(empsWithId12);
for (int i = index; i < empList.size(); i++) {
Emp emp = empList.get(i);
if (emp.getId() == 12) {
empsWithId12.add(emp);
} else {
break;
}
}
Note that the idea above can be greatly improved by moving the logic into a method.
Once you get the index of the the search key, you can go backwards or left of the list to find all other indexes with key equal to the search key
i want to make a connected list of Student elements. The class Student must have only two fields(name and codeNumber).I also created a method to compare the names and if the names are equals then i compare codeNumbers. this is because i must insert the elements low to high.
I created a class like:
class Student
private String name;
private int codeNumber;
public Student(String name, int AM){
this.name = name;
this.codeNumber = codeNumber;
}
public int compareTo(Student other){
int result;
if(other.name.compareTo(this.name) == 0){
result = 0;
return result;
}
if(other.name.compareTo(this.name) < 0){
result = 1;
return result;
}
if(other.name.compareTo(this.name) > 0){
result = -1;
return result;
}
if(other.codeNumber > this.codeNumber){
result = 1;
return result;
}
if(other.codeNumber < this.codeNumber){
result = -1;
return result;
}
}
public String getName(){
return name;
}
public int getcodeNumber(){
return codeNumber;
}
}
And here is the problem. I need a StudentList class to make my own list.
So i created the class but im not sure if i have the right fields in this class.
I created 4 fields:
1.private Student studentElement;
2.private StudentList next = null; a reference to the next element
3.private StudenList head; a reference to the start of the list
4.private int size = 0; to know the number of the elements
And here is my code:
class StudentList{
private Student studentElement;
private StudentList next = null;
private StudentList head;
private int size = 0;
public StudentList(Student listEl){
listElement = listEl;
}
public boolean containsElement(Student p){
StudentList position = head;
while(position != null){
if(StudentElement.getName().equals(p.getName()) && studentElement.getAM() == p.getAM()){
return true;
}
position = position.getNext();
}
return false;
}
}
This is my code. Can anyone tell me if i have the right types of fields in my class and help me creating an insert method.
Don't reinvent the wheel.
Prefer using java.util.LinkedList
and add the Student instances in the way you need. This will preserve the order in
which you add to the list
Use one of the Java library list implementations such as ArrayList or LinkedList which do precisely what you want, no point in rolling-your-own.
Also learn about Collections.sort() if needed.
Your Student class should also implement the Comparable interface which (by a stroke of luck) has the same signature as your existing compareTo method.
I have list of objects in an arraylist and I need to compare every objects with other objects available in the arraylist;
For Example:
Class Employee {
private String empname;
private Long empid;
private boolean empsex;
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public Long getEmpid() {
return empid;
}
public void setEmpid(Long empid) {
this.empid = empid;
}
public boolean isEmpsex() {
return empsex;
}
public void setEmpsex(boolean empsex) {
this.empsex = empsex;
}
}
public list<Employee> getEmpList() {
List<Employee> empList = new ArrayList<Employee>();
Employee emp = new Employee();
for(...) {
//insert values to emp object for n number of times;
}
empList.add(emp); //add emp.object to empList;
return empList;
}
Now while inserting these values to UI; Need to compare objects in the list; where any two or more objects matches with each other or not?
Based on the assumption that you want to eliminate the duplicates from the list and do not show duplicates on GUI.
Use Set collection, it automatically takes care of duplicates.
A collection that contains no duplicate elements. More formally, sets
contain no pair of elements e1 and e2 such that e1.equals(e2), and at
most one null element. As implied by its name, this interface models
the mathematical set abstraction.
Override equals() and hashcode() methods.
References:
On equals and hashcode in Java
Overriding equals and hashcode
You could override equals method for that class to compare the objects of the same class the way you want.
Default equals method:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Link about overriding equals:
http://javarevisited.blogspot.com/2011/02/how-to-write-equals-method-in-java.html
One simple method of doing this is to just do a double for loop.
public static List<Employee> getList(List<Employee> oldList)
{
List<Employee> empList = new ArrayList<Employee>();
for (int i = 0; i < oldList.size; i++)
{
for (int j = 0; j < oldList.size; j++)
{
//compare oldList.get(i) with oldList.get(j)
//if match, set some boolean
}
//if duplicate found, delete one copy, or add one to new list, etc
}
This allows you to go through each element in the outer loop, and compare to every other element in the inner loop.
I guess employee id (empid) is unique for each emplyee, yes?
If so, use a hash instead where empid is the key and employee object is the value.
Map<Long, Employee> empmap = new HashMap<Long, Employee>();
empmap.put(currentEmployee.getEmpid(), currentEmployee)