How to compose streams\Observables correctly - java

I have conceptual problem understanding how to compose between streams\Observables which have different return type.
Here is a draft method I'm trying to code:
public void findSeat() {
rx.Observable<GameObject> userObs = context.getUser();
rx.Observable<ActiveGame> gameObs = context.findGame();
rx.Observable.zip(userObs, gameObs, (userObj, game) -> {
User user = ...;
final List<Object> results = new ArrayList<Object>(3);
if(userObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-1);
return results;
}
...
...
//***********************************
// THE PROBLEM IS HERE:
// "context.getActiveGameManager().updateGame(game)" returns Observable<GameOBject> and not List<Object> like .zip() expects.
// because of that I cannot do:
// "return context.getActiveGameManager().updateGame(game);"
// How can I do this convertion from Observable<GameObject> to List<Object>
//************************************
context.getActiveGameManager().updateGame(game)
.map((gameObj) -> {
if(gameObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-2);
return (Observable<? extends Object>) results;
}
results.add(ErrorCodes.STATUS_OK);
results.add(user);
results.add(gameObj);
return gameObs;
});
return Observable.empty();
}).subscribe((results) -> {
int status = (int) results.get(0);
User user = (User) results.get(1);
ActiveGame game = (ActiveGame) results.get(2);
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
}
The flow is as follow:
1. emit 2 Observable using .zip method.
2. do some logic on the return value of streams and if it results in error-code --> put it in list and return it so "subscribe" can return the error to user.
3. if no error, emit another "update" method using flatMap() - and this is where I have my problem.
4. eventually, all the results should be processed in "subscribe" because this is the point I acknowledge the user about his request.
Hope it's clear enough...
by the way, I'm trying to learn rxJava, but it's very hard I find there are enough\good sources - can someone recommend to me the best way to learn it?? I trying looking at tutorials at Youtube, Wikipedia, Github...most of them teaches using Scala and other scripting languages - couldn't find anything in Java.
Thank you for everyone that put the effort trying understand it!!

I think you were almost there, but try breaking down the code inside your .zip lambda into smaller Rx operations. For example:
rx.Observable
.zip(userObs, gameObs, (userObj, game) -> {
// Combine the user & game objects and pass them to the
// next Rx operation.
return new UserAndActiveGame(userObj, game);
})
.filter(userAndActiveGame -> {
// Remove this filter if you want errors to make it to the subscriber.
return userAndActiveGame.getUserObj().getStatus() == ErrorCodes.STATUS_OK;
})
.flatMap(userAndActiveGame -> {
// Remove this check if you filter errors above.
if (userAndActiveGame.getUserObj().getStatus() != ErrorCodes.STATUS_OK) {
return Observable.just(new FindSeatResult(-1));
}
return context.getActiveGameManager().updateGame(userAndActiveGame.getGame())
.map(gameObj -> {
if (gameObj.getStatus() != ErrorCodes.STATUS_OK) {
return new FindSeatResult(-2);
}
User user =...; // Whatever you are doing to get this in your example code.
return new FindSeatResult(ErrorCodes.STATUS_OK, user, gameObj);
});
})
The following classes are used for passing intermediate and final results:
private class UserAndActiveGame {
private final GameObject userObj;
private final ActiveGame game;
public UserAndActiveGame(GameObject userObj, ActiveGame game) {
this.userObj = userObj;
this.game = game;
}
public GameObject getUserObj() {
return userObj;
}
public ActiveGame getGame() {
return game;
}
}
private class FindSeatResult {
private final int status;
private final User user;
private final ActiveGame game;
public FindSeatResult(int status) {
this(status, null, null);
}
public FindSeatResult(int status, User user, ActiveGame game) {
this.status = status;
this.user = user;
this.game = game;
}
public User getUser() {
return user;
}
public int getStatus() {
return status;
}
public ActiveGame getGame() {
return game;
}
}
Your subscriber then uses the packaged result similar to what you are already doing.
.subscribe((results) -> {
// You don't need this if you filter errors above.
if (findSeatResult.getStatus() == -1) {
return;
}
int status = findSeatResult.getStatus();
User user = findSeatResult.getUser();
ActiveGame game = findSeatResult.getGame();
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
By using the intermediate and final results classes instead of passing around your results in a List<Object> your code is much more forgiving to changes and the compiler will type check everything for you.

Related

Java Stream API with non-pure functions chain

Let's assume I have a class Person
public class Person {
private final String name;
private final int age;
private boolean rejected;
private String rejectionComment;
public void reject(String comment) {
this.rejected = true;
this.rejectionComment = comment;
}
// constructor & getters are ommited
}
and my app is something like that
class App {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("John", 10),
new Person("Sarah", 20),
new Person("Daniel", 30)
)
persons.forEach(p -> {
rejectIfYoungerThan15(p);
rejectIfNameStartsWithD(p);
// other rejection functions
}
}
private static void rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
p.reject("Too young")
}
}
private static void rejectIfNameStartsWithD(Person p) {
if (!p.isRejected() && p.getName().startsWith("D")) {
p.reject("Name starts with 'D'")
}
}
// other rejection functions
}
The thing is I don't like that I have to perform !p.isRejected() check in every rejection function. Moreover, it doesn't make sense to pass an already rejected person to next filters.
So my idea is to use a mechanism of Stream.filter and make something like
persons.stream().filter(this::rejectIfYoungerThan15).filter(this::rejectIfNameStartsWithD)...
And change signature for these methods to return true if a passed Person has not been rejected and false otherwise.
But it seems to me that it's a very bad idea to use filter with non-pure functions.
Do you have any ideas of how to make it in more elegant way?
When you change the check functions to only check the condition (i.e. not to call p.isRejected()) and return boolean, you already made the necessary steps to short-circuit:
private static boolean rejectIfYoungerThan15(Person p) {
if(p.getAge() < 15) {
p.reject("Too young");
return true;
}
return false;
}
private static boolean rejectIfNameStartsWithD(Person p) {
if(p.getName().startsWith("D")) {
p.reject("Name starts with 'D'");
return true;
}
return false;
}
usable as
persons.forEach(p -> {
if(rejectIfYoungerThan15(p)) return;
if(rejectIfNameStartsWithD(p)) return;
// other rejection functions
}
}
A Stream’s filter operation wouldn’t do anything other than checking the returned boolean value and bail out. But depending on the Stream’s actual terminal operation the short-circuiting could go even farther and end up in not checking all elements, so you should not bring in a Stream operation here.
Calling these methods from lambda is fine, however, for better readability, you can rename these methods to show what they are doing and return boolean, e.g.:
private boolean hasEligibleAge(Person p){..}
private boolean hasValidName(Person p){..}
Another approach would be to wrap these methods into another method (to reflect the business logic/flow), e.g.:
private boolean isEligible(Person p){
//check age
//check name
}
You should make Person immutable, and let the reject-methods return a new Person. That will allow you to chain map-calls. Something like this:
public class Person {
private final String name;
private final int age;
private final boolean rejected;
private final String rejectionComment;
public Person reject(String comment) {
return new Person(name, age, true, comment);
}
// ...
}
class App {
// ...
private static Person rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
return p.reject("Too young");
}
return p;
}
}
Now you can do this:
persons.stream()
.map(App::rejectIfYoungerThan15)
.map(App::rejectIfNameStartsWithD)
.collect(Collectors.toList());
If you want to remove rejected persons, you can add a filter after the mapping:
.filter(person -> !person.isRejected())
EDIT:
If you need to short circuit the rejections, you could compose your rejection functions into a new function and make it stop after the first rejection. Something like this:
/* Remember that the stream is lazy, so it will only call new rejections
* while the person isn't rejected.
*/
public Function<Person, Person> shortCircuitReject(List<Function<Person, Person>> rejections) {
return person -> rejections.stream()
.map(rejection -> rejection.apply(person))
.filter(Person::isRejected)
.findFirst()
.orElse(person);
}
Now your stream can look like this:
List<Function<Person, Person>> rejections = Arrays.asList(
App::rejectIfYoungerThan15,
App::rejectIfNameStartsWithD);
List<Person> persons1 = persons.stream()
.map(shortCircuitReject(rejections))
.collect(Collectors.toList());

Reactor Mono - execute parallel tasks

I am new to Reactor framework and trying to utilize it in one of our existing implementations. LocationProfileService and InventoryService both return a Mono and are to executed in parallel and have no dependency on each other (from the MainService). Within LocationProfileService - there are 4 queries issued and the last 2 queries have a dependency on the first query.
What is a better way to write this? I see the calls getting executed sequentially, while some of them should be executed in parallel. What is the right way to do it?
public class LocationProfileService {
static final Cache<String, String> customerIdCache //define Cache
#Override
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
//These 2 are not interdependent and can be executed immediately
Mono<String> customerAccountMono = getCustomerArNumber(customerId,location) LocationNumber).subscribeOn(Schedulers.parallel()).switchIfEmpty(Mono.error(new CustomerNotFoundException(location, customerId))).log();
Mono<LocationProfile> locationProfileMono = Mono.fromFuture(//location query).subscribeOn(Schedulers.parallel()).log();
//Should block be called, or is there a better way to do ?
String custAccount = customerAccountMono.block(); // This is needed to execute and the value from this is needed for the next 2 calls
Mono<Customer> customerMono = Mono.fromFuture(//query uses custAccount from earlier step).subscribeOn(Schedulers.parallel()).log();
Mono<Result<LocationPricing>> locationPricingMono = Mono.fromFuture(//query uses custAccount from earlier step).subscribeOn(Schedulers.parallel()).log();
return Mono.zip(locationProfileMono,customerMono,locationPricingMono).flatMap(tuple -> {
LocationProfileInfo locationProfileInfo = new LocationProfileInfo();
//populate values from tuple
return Mono.just(locationProfileInfo);
});
}
private Mono<String> getCustomerAccount(String conversationId, String customerId, String location) {
return CacheMono.lookup((Map)customerIdCache.asMap(),customerId).onCacheMissResume(Mono.fromFuture(//query).subscribeOn(Schedulers.parallel()).map(x -> x.getAccountNumber()));
}
}
public class InventoryService {
#Override
public Mono<InventoryInfo> getInventoryInfo(String inventoryId) {
Mono<Inventory> inventoryMono = Mono.fromFuture(//inventory query).subscribeOn(Schedulers.parallel()).log();
Mono<List<InventorySale>> isMono = Mono.fromFuture(//inventory sale query).subscribeOn(Schedulers.parallel()).log();
return Mono.zip(inventoryMono,isMono).flatMap(tuple -> {
InventoryInfo inventoryInfo = new InventoryInfo();
//populate value from tuple
return Mono.just(inventoryInfo);
});
}
}
public class MainService {
#Autowired
LocationProfileService locationProfileService;
#Autowired
InventoryService inventoryService
public void mainService(String customerId, String location, String inventoryId) {
Mono<LocationProfileInfo> locationProfileMono = locationProfileService.getProfileInfoByLocationAndCustomer(....);
Mono<InventoryInfo> inventoryMono = inventoryService.getInventoryInfo(....);
//is using block fine or is there a better way to do?
Mono.zip(locationProfileMono,inventoryMono).subscribeOn(Schedulers.parallel()).block();
}
}
You don't need to block in order to get the pass that parameter your code is very close to the solution. I wrote the code using the class names that you provided. Just replace all the Mono.just(....) with the call to the correct service.
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
Mono<String> customerAccountMono = Mono.just("customerAccount");
Mono<LocationProfile> locationProfileMono = Mono.just(new LocationProfile());
return Mono.zip(customerAccountMono, locationProfileMono)
.flatMap(tuple -> {
Mono<Customer> customerMono = Mono.just(new Customer(tuple.getT1()));
Mono<Result<LocationPricing>> result = Mono.just(new Result<LocationPricing>());
Mono<LocationProfile> locationProfile = Mono.just(tuple.getT2());
return Mono.zip(customerMono, result, locationProfile);
})
.map(LocationProfileInfo::new)
;
}
public static class LocationProfileInfo {
public LocationProfileInfo(Tuple3<Customer, Result<LocationPricing>, LocationProfile> tuple){
//do wathever
}
}
public static class LocationProfile {}
private static class Customer {
public Customer(String cutomerAccount) {
}
}
private static class Result<T> {}
private static class LocationPricing {}
Pleas remember that the first zip is not necessary. I re write it to mach your solution. But I would solve the problem a little bit differently. It would be clearer.
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
return Mono.just("customerAccount") //call the service
.flatMap(customerAccount -> {
//declare the call to get the customer
Mono<Customer> customerMono = Mono.just(new Customer(customerAccount));
//declare the call to get the location pricing
Mono<Result<LocationPricing>> result = Mono.just(new Result<LocationPricing>());
//declare the call to get the location profile
Mono<LocationProfile> locationProfileMono = Mono.just(new LocationProfile());
//in the zip call all the services actually are executed
return Mono.zip(customerMono, result, locationProfileMono);
})
.map(LocationProfileInfo::new)
;
}

Best way to iterate two lists and extract few things?

I have two classes as shown below. I need to use these two classes to extract few things.
public final class ProcessMetadata {
private final String clientId;
private final String deviceId;
// .. lot of other fields here
// getters here
}
public final class ProcMetadata {
private final String deviceId;
private final Schema schema;
// .. lot of other fields here
}
Now I have below code where I am iterating above two classes and extracting schema given a clientId.
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
}
}
return Optional.absent();
}
Is there any better way of getting what I need by iterating those two above classes in couple of lines instead of what I have? I am using Java 7.
You're doing a quadratic* search operation, which is inneficient. You can do this operation in constant time by first creating (in linear time) a mapping from id->object for each list. This would look something like this:
// do this once, in the constructor or wherever you create these lists
// even better discard the lists and use the mappings everywhere
Map<String, ProcessMetadata> processMetadataByClientId = new HashMap<>();
for (ProcessMetadata process : processMetadataList) {
processMetadataByClientId.put(process.getClientId(), process);
}
Map<String, ProcMetadata> procMetadataByDeviceId = new HashMap<>();
for (ProcMetadata metadata2 : procMetadataList) {
procMetadataByDeviceId.put(proc.getDeviceId(), proc);
}
Then your lookup simply becomes:
public Optional<Schema> getSchema(String clientId) {
ProcessMetadata process = processMetadataByClientId.get(clientId);
if (process != null) {
ProcMetadata proc = procMetadataByDeviceId.get(process.getDeviceId());
if (proc != null) {
return Optional.of(proc.getSchema());
}
}
return Optional.absent();
}
In Java 8 you could write it like this:
public Optional<Schema> getSchema(String clientId) {
return Optional.fromNullable(processMetadataByClientId.get(clientId))
.map(p -> procMetadataByDeviceId.get(p.getDeviceId()))
.map(p -> p.getSchema());
}
* In practice your algorithm is linear assuming client IDs are unique, but it's still technically O(n^2) because you potentially touch every element of the proc list for every element of the process list. A slight tweak to your algorithm can guarentee linear time (again assuming unique IDs):
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
// adding a break here ensures the search doesn't become quadratic
break;
}
}
return Optional.absent();
}
Though of course using maps ensures constant-time, which is far better.
I wondered what could be done with Guava, and accidentally wrote this hot mess.
import static com.google.common.collect.Iterables.tryFind
public Optional<Schema> getSchema(final String clientId) {
Optional<String> deviceId = findDeviceIdByClientId(clientId);
return deviceId.isPresent() ? findSchemaByDeviceId(deviceId.get()) : Optional.absent();
}
public Optional<String> findDeviceIdByClientId(String clientId) {
return tryFind(processMetadataList, new ClientIdPredicate(clientId))
.transform(new Function<ProcessMetadata, String>() {
String apply(ProcessMetadata processMetadata) {
return processMetadata.getDeviceId();
}
});
}
public Optional<Schema> findSchemaByDeviceId(String deviceId) {
return tryFind(procMetadataList, new DeviceIdPredicate(deviceId.get())
.transform(new Function<ProcMetadata, Schema>() {
Schema apply(ProcMetadata procMetadata) {
return processMetadata.getSchema();
}
});
}
class DeviceIdPredicate implements Predicate<ProcMetadata> {
private String deviceId;
public DeviceIdPredicate(String deviceId) {
this.deviceId = deviceId;
}
#Override
public boolean apply(ProcMetadata metadata2) {
return metadata2.getDeviceId().equalsIgnoreCase(deviceId)
}
}
class ClientIdPredicate implements Predicate<ProcessMetadata> {
private String clientId;
public ClientIdPredicate(String clientId) {
this.clientId = clientId;
}
#Override
public boolean apply(ProcessMetadata metadata1) {
return metadata1.getClientId().equalsIgnoreCase(clientId);
}
}
Sorry.

Java: message system needs to be able to pass various objects

I'm writing a messaging system to queue actions for my program to execute. I need to be able to pass various objects by the messages. I currently have a Msg object that accepts (Action enum, Data<?>...object). The Data object is intended to be a wrapper for any object I might pass.
Currently the Data object uses this code, with generics:
public class Data<T> {
private T data;
public Data(T data){
this.data = data;
}
public T getData(){
return data;
}
}
The Msg object takes Data<?>... type, so Msg has a Data<?>[] field.
If getData() is called on a Data<?> object, it returns the Object type. Obviously not ideal.
I need to be able to pass, say, Image objects as well as String objects. I'm certain there's a better way of passing arbitrary data.
The reason you're having trouble is that you're trying to get the static typing system of Java to do something that it can't. Once you convert from a Data<T> to a Data<?>, whatever T was is effectively lost. There's no clean way to get it back.
The quickest way to get it to work (from what you have right now) is to start throwing casts everywhere, like this:
Data<?> d = new Data("Hello");
String contents = (String)d.getData();
This is kind of a terrible idea, so let's go back to the drawing board.
If (ideally), you have all of the types you could ever need ahead of time (i.e. every Data is either a String or an Image or an Integer), then you can pretty easily (though it's a bit tedious) define a Sum type (aka a union if you're coming from C) of the different types of data you'll have to handle. As a class invariant, we assume that exactly one of the fields is non-null, and the rest are null. For this example I'll assume it can be either a String, an Image, or an Integer, but it's fairly simple to add or remove types from Data as necessary.
public class Data {
private Image imgData;
private String stringData;
private Integer intData;
public Data(Image img) {
this.imgData = img;
}
public Data(String stringData) {
this.stringData = stringData;
}
public Data(Integer intData) {
this.intData = intData;
}
public boolean isImage() {
return imageData != null;
}
public boolean isInteger() {
return intData != null;
}
public boolean isString() {
return stringData != null;
}
public Image asImage() {
if(! isImage()) throw new RuntimeException();
return imgData;
}
public Image asString() {
if(! isString()) throw new RuntimeException();
return stringData;
}
public Image asInt() {
if(! isInt()) throw new RuntimeException();
return intData;
}
}
One necessary side effect is that we cannot wrap null without causing exceptional behavior. Is this is desired, it isn't too difficult to modify the class to allow for it.
With this Data class, it's pretty easy to do if-else logic to parse it.
Data d = ....... //Get a data from somewhere
if(d.isImage()) {
Image img = d.asImage();
//...
} else if (d.isString()) {
String string = d.asString();
//...
} else if (d.isInteger()) {
Integer i = d.asInt();
//...
} else {
throw new RuntimeException("Illegal data " + d + " received");
}
If you call getData().getClass() you will get the class or type that was passed, which doesn't seem to me to be the same as an Object. You might not know what you are getting, but you can either find out or define a common interface for everything you might pass. You could for example, call toString() or getClass() on anything passed. Your question is that you are passing any conceivable object, so my question is what are you going to do with it? If you are going to serialize it into a database you don't need know anything about what type it is, otherwise you can test it or call a common interface.
public class PlayData {
class Msg {
private List<Data<?>> message = new ArrayList<Data<?>>();
public void addData(Data<?> datum) { message.add(datum); }
public void printTypes() { for ( Data<?> datum: message ) { System.out.println(datum.getData().getClass()); } }
}
class Data<T> {
private T value;
public Data(T value) { this.value = value; }
public T getData() { return value; }
}
class Listener {
public void receive(Msg msg) { msg.printTypes(); }
}
class Sender {
private Listener listener;
public Sender(Listener listener) { this.listener = listener; }
public void send(Msg msg) { listener.receive(msg); }
}
class MyPacket {
int i;
public MyPacket(int i) { this.i = i; }
}
public static void main(String[] args) throws Exception { new PlayData().run(); }
public void run() throws Exception {
Sender sender = new Sender(new Listener());
Msg msg = new Msg();
msg.addData(new Data<String>("testing") );
msg.addData(new Data<MyPacket>(new MyPacket(42)) );
sender.send(msg);
}
}

recursive call in looping a list

I m trying to loop a list of users to find a person. Every person has a friends list. So i m using recursive call to check if the person is in someones friends list.
My Junit test is look like
#Test
public void IsInFriendsCicle() throws UserAlreadyInFriendListException, NoFriendFoundException, UsersNotConnectedException {
User one = new UserImpl("John","Snow");
User two = new UserImpl("Richard","Gerns");
User three = new UserImpl("Natalie","Portman");
User four = new UserImpl("Brad","Pitt");
User five = new UserImpl("Angelina","Jolie");
one.addFriend(two);
two.addFriend(three);
three.addFriend(four);
four.addFriend(five);
assertTrue(one.isInFriendsCycle(five, one.getFriends(), new Stack()));
}
So as it can be seen here, i want to know if Angelina is in the friends list of john. So it supposed to give back true.
The responsible method is for that :
public boolean isInFriendsCycle(User userToFind, ArrayList<User> list, Stack stack){
Stack s = stack;
ArrayList<User> groupList = list;
if(groupList.contains(userToFind)){
return true;
}else{
for (User user : groupList) {
if(!s.contains(user)){
s.push(user);
if(user.getFriends().contains(userToFind)){
return true;
}else{
return isInFriendsCycle(userToFind, user.getFriends(), s);
}
}
}
}
return false;
}
So the class is :
public class UserImpl implements User{
private String name;
private String surname;
private static int count = 0;
private int id;
private ArrayList<User> friends;
private ArrayList<Message> messagebox;
final static Logger logger = Logger.getLogger(UserImpl.class);
public UserImpl(String name, String surname) {
this.name = name;
this.surname = surname;
this.id = ++count;
this.friends = new ArrayList<User>();
this.messagebox = new ArrayList<Message>();
}
#Override
public User addFriend(User person) throws UserAlreadyInFriendListException,IllegalArgumentException{
if(this.getFriends().contains(person)){
throw new UserAlreadyInFriendListException("user is already in the friendlist");
}else if(person == null || this.equals(person) ){
throw new IllegalArgumentException("parameter is null or user trying to add himself as friend");
}else{
this.getFriends().add(person);
person.getFriends().add(this);
logger.debug(this.name + " added the user "+person.getName());
return person;
}
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UserImpl other = (UserImpl) obj;
if (this.id != other.id) {
return false;
}
return true;
}
}
There is a problem with stack somehow. I m using it to mark the persons so i dont get in the infinitive loop. There is a reason for passing user.getFriends() so it should stay in that way.
Any help will be appreciated !
replace
return isInFriendsCycle(userToFind, user.getFriends(), s);
with
if (isInFriendsCycle(userToFind, user.getFriends(), s)) return true;
As you have it, you prematurely exit if you didn't find it in the first branch of the recursive call. You don't want that - you want to continue until you find it, or there is nothing left to search through.
As an aside - your unit test wasn't helping you as you made the nesting too deep. If you had a few unit tests, first testing a friend-friend, then a friend-friend-friend, etc., you would have started to see where it was going wrong.
Also, as another aside: I would not use Stack (it is a legacy class) use Deque instead, which is the Java 6+ replacement. However in this case I would use a Set<User> in the form HashSet<User> as it fits your use case and probably performance requirements. I would also make the list variable types a List<User> not an ArrayList<User> inside your UserImpl class and isInFriendsCycle method, just to concentrate on the contracts and not the implementation.
I'd implement it as follows:
private Set<User> friends = new HashSet<User>();
public void addFriend(User friend) {
friends.add(friend);
}
public boolean isImmediateFriend(User user) {
return friends.contains(user);
}
public boolean isInFriendNetwork(User user) {
Set<User> visited = new HashSet<>();
List<User> stack = new LinkedList<>();
stack.push(this);
while (!stack.isEmpty()) {
User current = stack.removeFirst();
if (current.isImmediateFriend(user)) {
return true;
}
visited.add(current);
for (User friend : current.getFriends()) {
// never visit the same user twice
if (!visited.contains(friend)) {
stack.addLast(friend);
}
}
}
return false;
}
Pseudocode for the recursive algorithm with marking:
IsFriendOf(A, B):
if A == B:
return True
B.Marked= True
for all C in B.Friends:
if not C.Marked and IsFriendOf(A, C):
B.Marked= False
return True
B.Marked= False
return False

Categories

Resources