Let's say I have a List of Employee Objects. The Employee Objects have a getDepartment method which returns a Department Object. I want to iterate through that list to find the department with the most Employees (i.e. the Department Object returned most often from getDepartment). What is the fastest way to do this?
public class Employee{
static allEmployees = new ArrayList<Employee>();
int id;
Department department;
public Employee(int id, Department department){
this.id = id;
this.department = department;
allEmployees.add(this);
}
public Department getDepartment(){
return department;
}
public static List<Employee> getAllEmployees(){
return allEmployees;
}
}
public class Department{
int id;
String name;
public Department(int id){
this.id = id;
}
public String getName(){
return name;
}
}
If there's two departments with an equal number of employees it doesn't matter which is returned.
Thanks!
create a map of department id -> counts.
that way you get a collection of all the counts by id. You can also maintain a max item that is a reference to the map entry with the highest count.
the algorithm would be something like:
1) Initialize a Map and a currentMax
2) loop through employees
3) for each employee, get its department id
4) do something like map.get(currentId)
a) if the current count is null, initialize it
5) increment the count
6) if the incremented count is > currentMax, update currentMax
This algorithm will run in O(n); I don't think you can get any better than that. Its space complexity is also O(n) because the number of counts is proportional to the size of the input.
If you wanted, you could create a class that uses composition (i.e. contains a Map and a List) and also manages keeping pointers to the Entry with the highest count. That way this part of your functionality is properly encapsulated. The stronger benefit of this approach is it allows you to maintain the count as you enter items into the list (you would proxy the methods that add employees to the list so they update the map counter). Might be overkill though.
Here's a vanilla Java 8 solution:
Employee.getAllEmployees()
.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()))
.entrySet()
.stream()
.max(Comparator.comparing(Entry::getValue))
.ifPresent(System.out::println);
It passes through the list of employees at most twice. An equivalent solution using jOOλ, if you're willing to add a third-party dependency, is this:
Seq.seq(Employee.getAllEmployees())
.grouped(Employee::getDepartment, Agg.count())
.maxBy(Tuple2::v2)
.ifPresent(System.out::println);
(Disclaimer: I work for the company behind jOOλ)
I would do something like this using Guava:
Multiset<Department> departments = HashMultiset.create();
for (Employee employee : employees) {
departments.add(employee.getDepartment());
}
Multiset.Entry<Department> max = null;
for (Multiset.Entry<Department> department : departments.entrySet()) {
if (max == null || department.getCount() > max.getCount()) {
max = department;
}
}
You would need a correct implementation of equals and hashCode on Department for this to work.
There's also an issue here that mentions the possibility of creating a "leaderboard" type Multiset in the future that would maintain an order based on the count of each entry it contains.
Since you just want to count employees, it's relatively easy to make a map.
HashMap<Department, Integer> departmentCounter;
that maps Departments to the number of employees (you increment the count for every employee). Alternatively, you can store the whole Employee in the map with a list:
HashMap<Department, List<Employee>> departmentCounter;
and look at the size of your lists instead.
Then you can look at the HashMap documentation if you don't know how to use the class:
http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html
Hint: you will need to use HashMap.keySet() to see which departments have been entered.
I would do it like that, modulo == null and isEmpty checks:
public static <C> Multimap<Integer, C> getFrequencyMultimap(final Collection<C> collection,
final Ordering<Integer> ordering) {
#SuppressWarnings("unchecked")
Multimap<Integer, C> result = TreeMultimap.create(ordering, (Comparator<C>) Ordering.natural());
for (C element : collection) {
result.put(Collections.frequency(collection, element), element);
}
return result;
}
public static <C> Collection<C> getMostFrequentElements(final Collection<C> collection) {
Ordering<Integer> reverseIntegerOrdering = Ordering.natural().reverse();
Multimap<Integer, C> frequencyMap = getFrequencyMultimap(collection, reverseIntegerOrdering);
return frequencyMap.get(Iterables.getFirst(frequencyMap.keySet(), null));
}
There is also CollectionUtils.getCardinalityMap() which will do the job of the first method, but this is more flexible and more guavish.
Just keep in mind that the C class should be well implemented, that is have equals(), hashCode() and implement Comparable.
This is how you can use it:
Collection<Dummy> result = LambdaUtils.getMostFrequentElements(list);
As a bonus, you can also get the less frequent element with a similar method, just, feed the first method with Ordering.natural() and do not reverse it.
Related
So I have this class:
public class Seat {
private Long id;
private float positionX;
private float positionY;
private int numOfSeats;
private String label;
//getters and setters
}
I have List of Seat class on:
List<Seat> seatList = // get data from repository;
I also have this arraylist contains list of ids:
List<Long> idList; // for example : [1, 2, 3]
I want to filter seatList so that the filtered ArrayList does not contain a Seat object with id from idList, so I tried to use stream:
List<Seat> filteredSeat = seatList.stream()
.filter(seat -> {
// function to filter seat.getId() so it would return the Seat object with id that does not equals to ids from idList
})
.collect(Collectors.toList());
I cant find the correct function to do it. Does anyone have suggestion for me to try?
You want to use the overriden method from Collection#contains(Object) with the negation implying the id was not found in the List.
Set<Seat> filteredSeat = seatList.stream()
.filter(seat -> !idList.contains(seat.getId()))
.collect(Collectors.toSet());
Few notes:
You want to use Set<Long> instead of List<Long> for an efficient look-up. Moreover, it doesn't make sense to have duplicate values among ids, so Set is a good choice.
Collectors.toSet() results Set, so the Stream's return type is Set<Seat>.
The most simple solution for be a for-each loop in which you check each idList against the Seat's id.
perhaps something like
List<Seat> FilteredList;
for ( Seat CurSeat : seatList ){
for(int i = 0; i < idList.size(); i++){
and if the ID of CurSeat is part of idList, it doesn't get added to the new List.
This is definitely not the simplest way, but if you're looking for something easy, this is probably it.
Hope this helped!
Assuming you implement the equals method accordingly (like the doc mentions it), there is a much shorter solution:
seatList.stream()
.distinct()
.collect( Collectors.toList() );
#Data
class Person {
private String fname;
private String lname;
private List<String> friends;
private List<BigDecimal> ratings;
...
}
#Data
class People {
private List<Person> people;
...
}
suppose i have the above classes, and need to figure out what is the most number of friends any one person has. I know the pre-streams way of doing it... by looping over every element in the list and
checking if friends.size() is greater than the currently stored number of longest list. is there a way to streamline the code with streams? something like this answer?
Compute the max Person contained in People according to a comparator that relies on the size of Person.getFriends() :
Optional<Person> p = people.getPeople()
.stream()
.max(Comparator.comparingInt(p -> p.getFriends()
.size()));
Note that you could also retrieve only the max of friends without the Person associated to :
OptionalInt maxFriends = people.getPeople()
.stream()
.mapToInt(p -> p.getFriends()
.size())
.max();
You can declare the following method in Person class:
public int getNumberOfFriends(){
return friends.size();
}
and then use it in a stream like this:
Optional <Person> personWithMostFriends = people.stream().max(Comparator.comparingInt(Person::getNumberOfFriends));
With this approach you will get the Person object with the most friends, not only the maximum number of friends as someone suggested.
Your question already answered. but i add this for one thing. if your list of person size large and if you have multi-core pc and you want to used this efficiently then use parallelstream().
To get person:
Person person = people.getPeople()
.parallelStream()
.max(Comparator.comparingInt(p-> p.getFriends().size()))
.orElse(null);
To get size:
int size = people.getPeople()
.parallelStream()
.mapToInt(p -> p.getFriends().size())
.max().orElse(0);
I have one problem statement: Suppose there is Employee class and a class has to be designed which provides get and put methods to access and add employees. Employee class is a third party class so we can't make changes in it. There is a memory limit like max 50k employees can be stored. Now if while adding employee if memory limit is reached then remove one employee by following any of the below approaches and add the new employee:
on the basis of access frequency
on the basis of timestamp. one with the old timestamp should be removed
I can think of taking two hashmaps: - one for storing employee objects and one with the employees with key and timestamp or frequency as value.
But this is not the optimal solution. Please provide the valuable suggestions on which data structure can be used to solve this problem.
Just use a normal collection for storing your Employee. When adding a new employee and you detect that the memory is full, you just determine the employee with the smallest sort order criteria and remove it from the database.
Since you did not provide the Employee class I declare here an example:
class Employee {
int frequency;
Instant lastAccess;
}
Your database (can also be LinkedList):
private List<Employee> myDatabase = new ArrayList<>();
The comporator for evaluating the employee with the smallest criteria (first frequency is compared, if equals the lastAccess is compared:
public int compare( Employee emp1, Employee emp2 )
{
int result = emp1.frequency - emp2.frequency;
if ( result == 0 )
{
result = emp1.lastAccess.compareTo( emp2.lastAccess );
}
return result;
}
And here the 'main' method which inserts a new employee into your database (the implementation of isMemoryFull is left to you):
private void insertEmp( Employee emp)
{
if ( isMemoryFull() )
{
Employee minEmp = myDatabase.stream().min( this::compare ).get();
myDatabase.remove( minEmp);
}
myDatabase.add( emp );
}
Note: This works with Java8 lambdas and streams. For java < 8 you have to implement the compare method formally as Comparator and use Collections.sort instead the stream().min() method.
I didn't find proper solution for the below scenario. I have employee names and location. In each location many employees can work.
Example: assume that employee names are unique so I consider it as a key and value as location.
TreeMap<String,String> t=new TreeMap<String,String>();
t.put(mike, Houston);
t.put(arian, Houston);
t.put(John, Atlanta);
Well my scenario is i have to write my own comparator where location is sorted first and when there are multiple locations of same name then they need to be sorted by employees. Any kind of help is appreciated.
you need a structure, and compareTo:
public class EmpLoc implements Comparable<EmpLoc> {
String employee;
String location;
public EmpLoc (String _employee, String _location)
{
employee=_employee;
location=_location;
}
#Override
public int compareTo(EmpLoc other)
{
int last = this.location.compareTo(other.location);
return last == 0 ? this.employee.compareTo(other.employee) : last;
}
}
The problem is in your data structure. TreeMap ensure your keys are always sorted in an order, but your key doesn't have full information you need to sort. Instead what you need is probably
TreeSet<Employee> employees = new TreeSet<>(employeeComparator);
where Employee is:
public class Employee {
private String name;
private String location;
/* getters & setters omitted */
}
Now you can create a comparator for Employee
You can use similar structure:
Map<String, List<String>> map = new TreeMap<>(<your_own_comparator_for_locations_or_default_one>);
This is Multimap, and this is implementation by conventional means, but also there are third-party implementation, e.g. Guava. Guava has some sorted, synchronized and immutable implementations of multimaps, you can use them by default or to see how to do some things.
You can put values like below:
public void putEmployees(String location, String employee) {
List<String> employees = map.get(location);
if (employee == null) {
employees = new ArrayList<>();
}
employees.add(employee);
Collections.sort(employees, <your_own_comparator_for_employees_or_default_one>);
map.put(location, employees);
}
there is a dependent list
Dependents contains
String emp_Id, name etc,
List<Dependent> dependentList;
dependentList contains all the dependent information of an employee.
how to get the list of dependents by providing the emp_Id ?
for example an employee will have 2 or 3 dependents.
ok i dont want to loop over it.
i tried binary search on list using comparator but it does not return the desired data.
already i will loop over the employee list... subsequently i should get the depends of the particular employee...
what will be the best & efficient solution ?
Binary search works only if the list is sorted according to the comparator. For lists that are not sorted or sorted according to other criteria, you have to filter them.
Either loop though the list and do whatever you want to do in the loop body
Or use a filter functionality from a library
If you want to filter, then I recommend Google Collections (or Google Guava, which is a superset of Google collections):
Collection<Dependent> filtered = Collections2.filter(dependentList, new Predicate<Dependent>() {
public boolean apply(Dependent from) {
return from != null && from.getId().equals(id_so_search_for);
}
}
Of course, you are not restricted to .equals(), but can match according to any operation required (e.g. by regular expression).
If searches for one kind of data heavily outweight searches for any other kind of data, then storing them in a Map<kind-of-id, Dependent> may be a good choice as well. You still can retrieve a collection of all stored objects using Map.values().
If one key maps to several items, then either use a Map<kind-of-id, Collection<Dependent>> or (better) consider using existing Multimap functionality: com.google.common.collect.Multimap or org.apache.commons.collections.MultiMap (note that Apache Commons does not have a genericized version of this).
You want to model relationships. I guess, you have the basic dependencies:
Supervisor is-a Employee
Supervisor has-many Employees (Dependants in your case)
So a very basic implementatin could go like this:
public class Employee {
int emp_id;
// more fields, more methods
}
public class Supervisor extends Employee {
private List<Employee> dependants = new ArrayList<Employee>();
// more fields, more methods
public List<Employee> getDependants() {
return dependants;
}
}
public class StaffDirectory {
private Map<Integer, Employee> staff = new HashMap<Integer, Employee>();
public static List<Employee> getAllDependantsOf(int employeeId) {
Employee employee = staff.get(employeeId);
if (employee instanceof Supervisor) {
return ((Supervisor) employee).getDependants());
} else {
return Collections.emptyList();
}
}
}
What have you tried so far? Do you have anything written?
Here is a general guess:
int employeeToFind = 10;//the id to search for
for(Dependant dep : dependentList ) {
if(dep.getEmployeeId() == employeeToFind) {
//do something
}
}
You could also store dependents in a Hashtable<Integer employeeId,List<Dependent>>(); keyed by EmployeeId for an easy lookup.
As alzoid mentioned, a HashMap or HashTable is the perfect data structure for this task. If you have any chance to load your instances of Dependent into such an object, do so.
Still, have this delicious code:
String emp_Id //IDs are usually integer, but I'll go with your example
List<Dependent> dependentList; //assume this is populated
List<Dependent> desiredSublist = new ArrayList<Dependent>();
for(Dependent dep:dependentList){
//make sure to compare with equals in case of Id being String or Integer
if(dep.getId().equals(emp_Id)){
desiredSubList.add(dep);
}
}
//desiredSublist now contains all instances of Dependent that belong to emp_Id.