I have a Mentor class in which I have an ID for each mentor and an ArrayList of mentee IDs, like this:
public class Mentor {
int mentorId;
ArrayList<Integer> mentees = new ArrayList<>();
public Mentor(int mentorId, ArrayList<Integer> mentees) {
this.mentorId = mentorId;
this.mentees = mentees ;
}
}
The problem is that some mentees can be mentors too.
I would like to somehow get a count of all of the mentees associated to the top mentor as well as how many mentors are under the top mentor.
So, basically, if a mentor has a mentee, who is also a mentor, then this mentor's mentees are also associated to the top mentor.
So, my thinking was to loop through the mentee-list and see if any of the id's match an ID of Mentor. If true, Add this mentor's list of mentees to a list and loop again, but this will not work dynamically.
My main class looks something like this:
ArrayList<Mentor> mentors = new ArrayList<>();
ArrayList<Integer> mentees = new ArrayList<>();
ArrayList<Integer> mentees2 = new ArrayList<>();
mentees.add(2);
mentees.add(3);
mentees2.add(4);
mentees2.add(5);
//[1,{2,3}]
mentors.add(new Mentor(1, mentees));
//[2,{4,5}]
mentors.add(new Mentor(2, mentees2));
int mentorCount = 0;
int menteeCount = 0;
for (Mentor mentor : mentors) {
for (Integer mentee : mentees) {
mentorCount++;
if (mentee == mentor.mentorId){
mentorCount++;
//add each mentee to arraylist and start the process again, but is there an easier way to do this.
}
}
}
I was wondering if there is a way of solving this, maybe using streams?
I would recommend using good object oriented design, you shouldn't just use integer id's like that, because in this situation you could simply make an ArrayList of Person objects Where Mentees, and Mentors Inherit from Person. Then you can check if a Person is an instance of Mentee versus Mentor:
for (Person p : people) {
if (p instanceof Mentor)
{
// Mentor logic
}
if (p instanceof Mentee)
{
// Mentee Logic
}
}
Firstly, let's briefly recap the task.
You have a set of mentors, each mentor has a collection of mentees. Some of them might happen also to be mentors and so on.
Class design
From the perspective of class design, the solution is pretty simple: you need only one class to describe this relationship - Mentor. And each Mentor should hold a reference to a collection of Mentors (not integer ids).
In your domain model, as you described it, there's no substantial difference between a mentor and a mentee. Mentor points to other mentors - is a simplest way to model that. Mentee is just an edge case, a mentor which has an empty collection of mentors.
You don't need to include classes and features in your application that doesn't bring benefit.
Data structure
From the perspective of data structures, this problem can be described very well with an acyclic disjointed Graph.
Acyclic because if we consider the relationship between mentors when a mentor could indirectly point at themself (i.e. a mentor N has a mentee, with in tern points to another mentee that happens to be also a mentor of mentor N) the task becomes ambiguous. Therefore, I'm making an assumption that no one can be a mentor to himself.
I also depicted this data structure as disjointed because mentors in this model (as well as in real life) can form isolated groups, which in graph theory called connected components. That means that there could be several mentors with the same count of mentee, which happens to be the largest.
Depth first search
In order to find all the vertices (mentors) connected with a particular mentor, we have a classic traversal algorithm, which is called Depth first search (DFS). The core idea of DFS is to peek a single neighbor of the given vertex, then in turn peek one of its neighbors and so on until the hit the vertex that doesn't point to the other vertex (i.e. maximum depth is reached). And then it should be done with every other neighbors of the previously visited vertices.
There are two ways to implement DFS.
Iterative approach
For that, we need a stack. It will store all unvisited neighbors of the previously encountered vertexes. The last vertex from each list of neighbors in every branch will be explored first because it will be placed on the top of the stack. Then it will get removed from the stack and it's neighbors will be placed on the stack. This process repeats in a loop until the stack contains at least one element.
The most performant choice of collection for the stack is ArrayDeque.
Because this approach require continuous modification of the stack by adding and removing vertices, it isn't suitable to be implemented with Stream IPA.
Recursive approach
The overall principle is the same as described above, but we don't need to provide a stack explosively. The call stack of the JVM will be utilized for that purpose.
With this approach, there's also some room to apply streams. For that reason, I've chosen the recursive implementation. Also, its code is probably a bit easier to understand. But keep in mind that recursion has certain limitations, especially in Java, and not suitable for processing a large set of data.
Recursion
A quick recap on recursion.
Every recursive implementation consists of two parts:
Base case - that represents a simple edge-case for which the outcome is known in advance. For this task, the base case is the given vertex has no neighbors. That means menteesCount of this vertex needs to be set to 0 because it has no mentee. And the return value for the base case is 1 because this vertex, in turn, is a valid mentee of another vertex that holds a reference to it.
Recursive case - a part of a solution where recursive calls a made and where the main logic resides.
The recursive case could be implemented using streams and entails recursive invocation of the for every neighbor of the given vertex. Each of these values will contribute to the menteesCount of the given vertex.
The value returned by the method will be menteesCount + 1 because for the vertex which triggered this method call, the given vertex will be a mentee as well as its mentees.
Implementation
Class mentor basically serves as a vertex of the graph. Each vertex has a unique id and collection of adjacent vertexes.
Also, in order to reuse values obtained by performing DFS I've added a field menteesCount which is initially initialized to -1 in order to distinguish between vertices that has no adjacent vertices (i.e. menteesCount has to be 0) and vertices which value wasn't calculated. Every value will be established only ones and then reused (another approach will be to utilize a map for that purpose).
Method getTopMentors() iterates over the collection of vertices and invokes DFS for every vertex which value wasn't calculated yet. This method returns a list of mentors with the highest number of associated mentees
Method addMentor() that takes a vertex id, and id of its neighbors (if any) was added in order to interact with the graph in a convenient way.
Map mentorById contains every vertex that was added in the graph and, as its name suggests, allows retrieving it based on the vertex id.
public class MentorGraph {
private Map<Integer, Mentor> mentorById = new HashMap<>();
public void addMentor(int mentorId, int... menteeIds) {
Mentor mentor = mentorById.computeIfAbsent(mentorId, Mentor::new);
for (int menteeId: menteeIds) {
mentor.addMentee(mentorById.computeIfAbsent(menteeId, Mentor::new));
}
}
public List<Mentor> getTopMentors() {
List<Mentor> topMentors = new ArrayList<>();
for (Mentor mentor: mentorById.values()) {
if (mentor.getCount() != -1) continue;
performDFS(mentor);
if (topMentors.isEmpty() || mentor.getCount() == topMentors.get(0).getCount()) {
topMentors.add(mentor);
} else if (mentor.getCount() > topMentors.get(0).getCount()) {
topMentors.clear();
topMentors.add(mentor);
}
}
return topMentors;
}
private int performDFS(Mentor mentor) {
if (mentor.getCount() == -1 && mentor.getMentees().isEmpty()) { // base case
mentor.setCount(0);
return 1;
}
int menteeCount = // recursive case
mentor.getMentees().stream()
.mapToInt(m -> m.getCount() == -1 ? performDFS(m) : m.getCount() + 1)
.sum();
mentor.setCount(menteeCount);
return menteeCount + 1;
}
public static class Mentor {
private int id;
private Set<Mentor> mentees = new HashSet<>();
private int menteesCount = -1;
public Mentor(int id) {
this.id = id;
}
public boolean addMentee(Mentor mentee) {
return mentees.add(mentee);
}
// getters, setter for menteesCount, equeals/hashCode
}
}
An example of the graph used as a demo.
main() - the code models the graph shown above
public static void main(String[] args) {
MentorGraph graph = new MentorGraph();
graph.addMentor(1, 3, 4);
graph.addMentor(2, 5, 6, 7);
graph.addMentor(3, 8, 9);
graph.addMentor(4, 10);
graph.addMentor(5, 11, 12);
graph.addMentor(6);
graph.addMentor(7, 13, 14);
graph.addMentor(8);
graph.addMentor(9, 16, 17, 18);
graph.addMentor(10);
graph.addMentor(11, 18);
graph.addMentor(12);
graph.addMentor(13);
graph.addMentor(14, 19, 20);
graph.addMentor(15);
graph.addMentor(16, 21, 22);
graph.addMentor(17);
graph.addMentor(18);
graph.addMentor(19);
graph.addMentor(20);
graph.addMentor(21);
graph.addMentor(22);
graph.getTopMentors()
.forEach(m -> System.out.printf("mentorId: %d\tmentees: %d\n", m.getId(), m.getCount()));
}
Output
mentorId: 1 mentees: 10
mentorId: 2 mentees: 10
Use Person and Mentor and Mentee subclasses as suggested by acornTime, define mentees as a list of Person and the information you want becomes simple to get:
import java.util.*;
import java.util.stream.Stream;
public class Main{
public static void main(String[] args) {
ArrayList<Person> mentees = new ArrayList<>();
mentees.add(new Mentee(11));
mentees.add(new Mentee(12));
mentees.add(new Mentor(2, new ArrayList<>()));
mentees.add(new Mentee(13));
mentees.add(new Mentee(14));
mentees.add(new Mentor(3, new ArrayList<>()));
mentees.add(new Mentor(4, new ArrayList<>()));
mentees.add(new Mentor(5, new ArrayList<>()));
Mentor mentor = new Mentor(1, mentees);
System.out.println(mentor.menteesCount());
System.out.println(mentor.mentorsInMentees().count());
}
}
interface Person {
int getId();
}
class Mentor implements Person{
private final int mentorId;
private List<Person> mentees = new ArrayList<>();
public Mentor(int id, ArrayList<Person> mentees) {
mentorId = id;
this.mentees = mentees ;
}
#Override
public int getId() {
return mentorId;
}
public List<Person> getMentees() {
return mentees;
}
public int menteesCount() {
return mentees.size();
}
public Stream<Person> mentorsInMentees(){
return mentees.stream().filter(m -> (m instanceof Mentor));
}
}
class Mentee implements Person{
private final int menteeId;
public Mentee(int id) {
menteeId = id;
}
#Override
public int getId() {
return menteeId;
}
}
Test it online here
You should do something like a depth-first or breadth-first search (*):
Maintain a Set<Integer> containing all the people you have already seen.
Maintain a queue of some kind (e.g. an ArrayDeque), of people you are going to check.
Put the first person (or any number of people, actually) into this queue.
Then, while the queue is not empty:
Take the next person in the queue
If you've already seen them, go to the next item in the queue
If you've not already seen them, put the person into the seen set; add all of their mentees into the queue
That's it. The number of people at the end is the size of the seen set.
(*) Whether you do depth-first or breadth-first search depends on which end of the queue you add mentees to: adding them to the same end that you remove them from results in depth-first search; adding them to the other end results in breadth-first search. If you don't care which, choose either.
I'm facing a weird behavior in my Java code using List.
The code is very simple, I have a List of Object called AccessRequest which comes from a database and I'm using this first List to create a new one but with a filter to select only a few objects.
Here is the code :
private void updateCommentIfNeeded() {
List<AccessRequest> accessRequestList = getAllRequest();
List<AccessRequest> commentsList = getCommentsListProcessedManually(accessRequestList);
}
public List<AccessRequest> getCommentsListProcessedManually(List<AccessRequest> accessRequests) {
accessRequests.removeIf(ar -> !ar.getComment().equals("To be processed manually"));
if (accessRequests.size() != 0) {
SQLServerConnection sqlServerConnection = new SQLServerConnection(sqlServerUrl);
accessRequests.removeIf(ar -> !sqlServerConnection.emailExists(ar.getEmail()));
}
return accessRequests;
}
I'm supposed to get a second List only containing the objects that has their comments to To be processed manually, which I do. But the weird part is that the first List also takes the value of the second as if I wrote accessRequestList = commentsList but there is no such thing and I'm using local variable.
Ex :
I have 3 objects in my first List, but only one containing the required comment
Both list ends with containing the only objects containing the comment
I'm kind of lost here if anyone has an idea !
Your method getCommentsListProcessedManually modifies the list you're passing. I believe you're operating under the assumption that passing the list as a parameter somehow creates a copy of the list, whereas what is actually happening is that a reference to the list is passed by value.
There are several ways to solve this, but the easiest is to simply create a copy of your input list at the start of your method:
public List<AccessRequest> getCommentsListProcessedManually(List<AccessRequest> input) {
List<AccessRequest> accessRequests = new ArrayList<>(input);
accessRequests.removeIf(ar -> !ar.getComment().equals("To be processed manually"));
if (accessRequests.size() != 0) {
SQLServerConnection sqlServerConnection = new SQLServerConnection(sqlServerUrl);
accessRequests.removeIf(ar -> !sqlServerConnection.emailExists(ar.getEmail()));
}
return accessRequests;
}
You could also use the Stream API for this (using the filter operation), but that's quite a bit trickier in this situation.
You are passing a reference of the list to the method getCommentsListProcessedManually.
So accessRequestList and the one passed as a parameter are the same, hence any operation done to the list is done to the same list.
You can create a copy of the list before passing it as a parameter:
List<AccessRequest> newList = new ArrayList<AccessRequest>(accessRequestList);
I really didn't want to resort to asking, however I'm at a dead end. I'm trying to build an array of objects stored within a hashmap into a single array. I'm building a minecraft plugin, and I need to be able to do this in order to reset all players to their natural state. However, for whatever reason, I can't seem to actually parse the Spectator[] array into individual pieces.
The goal is simply to allow more than 1 person to spectate. Here's my code:
public class EagleEye extends JavaPlugin implements Listener{
public HashMap<Spectatee, Spectator[]> spec = new HashMap(Spectatee, Spectator[]);
public HashMap<Spectatee, Spectator[]> orinven = new HashMap<Spectatee, Spectator[]>;
public HashMap<Spectatee, Spectator[]> eeinven = new HashMap<Spectatee, Spectator[]>;
#Override
public void onEnable()
{
//TODO:Who knows.
}
#Override
public void onDisable()
{
//TODO:Spec off any players being spectated and spectating.
Spectator[] frickinhell = spec.get(key));
//Creates a master list of all spectators by uuid
for(Spectator spec : spec.get(Spectator.class))
{
master.add(spec);
}
for(Object spec : master.toArray())
{
//Verify the player is online
if(Bukkit.getPlayer(master)
{
//Verify the player is still spectating
if(tators.get(uuid) == true)
{
//Stop spectating
tators.put(uuid, false);
}
}
}
}
I understand that much of this code is broken. However, my main concern is taking Spectator[] stored within all instances of Spectators[] stored within the hashmap and resetting their values to their defaults. Once I can access each individual instance of each object itself, I can reset their respective values using setters.
Cheers.
In spec.get(Spectator.class), Spectator.class doesn't match the type of your key, which is Spectatee. Therefore, it returns null.
You should pass an instance of Spectatee to spec.get() if you want to have a chance of getting a non-null value.
If you want to collect all the Spectators regardless of their key, you can iterate over the values of the Map :
for (Spectator[] value : spec.values())
for(Spectator spec : value)
{
master.add(spec);
}
I am working on website with games and I have map of players and their virtual tables.
private final ConcurrentMap<Player, List<Table>> tableOfPlayers = new ConcurrentHashMap<>();
and method to remove table
private void removeTable(Player player,Table table) {
if(tableOfPlayers.get(player).size() == 1) {
tableOfPlayers.remove(player);
} else {
tableOfPlayers.get(player).remove(table);
}
}
Is there any good way to solve this check-then-act idiom, because now it isn't thread-safe.
I know that I can synchronize both add and remove method, but I am wondering if it is possible to make it better. The reason why I check if size is equal to 1 is that if player have only one active table and I decide to remove I no longer need this player in my map.
I want to create a Queue which should not allow duplicate elements and I should be able to access elements of this queue based on index. Please let me know how should I implement this?
Well it is clear that Java doesn't have the exact data structure matching your specification and requirement. The closest that can match your requirement is probably a LinkedHashSet. It is basically a Set (matching your unique items requirement) whose elements are kept in insertion-order (like a Queue) and to get an element by index you can use set.toArray() to get an array or create a list out of the set (however it will cost cost some extra memory).
I am planning to use ConcurrentLinkedQueue for my problem. Here is the sample code
import java.util.concurrent.ConcurrentLinkedQueue;
public class FinalQueue {
private ConcurrentLinkedQueue<String> queue;
public FinalQueue()
{
queue = new ConcurrentLinkedQueue<String>();
}
public synchronized void enqueue(String ipAddress)
{
if(!queue.contains(ipAddress))
queue.add(ipAddress);
}
public String dequeue()
{
return queue.poll();
}
public String toString()
{
return "" + queue;
}
/**
* #param args
*/
public static void main(String[] args) {
FinalQueue queue = new FinalQueue();
queue.enqueue("1.2.3.4");
queue.enqueue("2.3.4.5");
queue.enqueue("1.1.1.1");
queue.enqueue("1.2.3.4");
System.out.println(queue.toString());
System.out.println("Dequeue..." + queue.dequeue());
System.out.println("Dequeue..." + queue.dequeue());
System.out.println(queue.toString());
}
}
You could always just use an ArrayList. It's good for accessing elements based on index and when adding elements you can always just check if the ArrayList contains the element to be added. My initial instinct was to use a Set for the disallowing of duplicates, but the elements are Sets are not indexed. If you can find a way to index the elements in Sets, then that would be my recommendation.
Don't call it a queue because by definition a queue only is a first in first out data structure.
Depending upon your input values, i believe you should use an array and a hash function. The hash determines which index an element is located using its value and vice versa i.e. when given an index it returns the value contained in it.
Since you are using a hash, the repetition is avoided when a collision occurs i.e. you can check if a value previously existed in an index and if it's the same value or not.
C++ stl has a good class for set though java i don't think have one. But the point is set does not offer index based retrieval.