Java/Android studio : For loop - Same data shows multiple times - java

So I am trying to create this page that compares a user's interest with other users and shows the list of all those users.. Now, with the for loop i created, one particular user's name repeats until the end of the loop. I only one one name per username to appear on the textfield.. However, I don't know how to do that.. Here's my code for showing users with common interests:
Realm realm= Realm.getDefaultInstance();
RealmResults<interests> result=realm.where(interests.class).findAll();
RealmResults<Users> user=realm.where(Users.class).findAll();
for(int i=0;i<result.size();i++)
{
for(int j=0;j<result.size();j++)
{
if(result.get(i).getId().equals(userid))
{
if(result.get(i).getInterest().equals(result.get(j).getInterest()))
{
if(!result.get(j).getId().equals(userid)) {
users = result.get(j).getId();
interestss.append("Interests :" + result.get(i).getInterest());
}
}
id.append("\n"+users);
}
}
}

for(int i=0;i<result.size();i++)
{
for(int j=0;j<result.size();j++)
{
if(result.get(i).getId().equals(userid))
{
if(result.get(i).getInterest().equals(result.get(j).getInterest()))
I'm almost 98% sure that you shouldn't even need to write this kind of code if you use Realm's query system and a link query, instead of looping and comparing things manually.
RealmResults<Interests> interests = realm.where(Interests.class)
.equalTo("user.userId", userId)
.findAll();
Which should be possible if you have a backlink from Interests to Users.
// in Interests class
#LinkingObjects("interest")
private final RealmResults<User> user = null;

Related

Is it necessary to create 151 connections to the server when displaying 150 images in a RecyclerView, stored in Firebase Storage's 'Images' folder?

Suppose you have 150 images stored in the images folder within your Firebase Storage. Each image should have a title, which will be inserted during the uploading process. Now, let's say you want to display all of these images in a RecyclerView. In this scenario, you would need to create 151 separate connections to the server instead of just one. This may seem counterintuitive, so allow me to explain.
The first connection is to retrieve all the images using the following line of code: FirebaseStorage.getInstance().getReference().child("Images/").listAll(). This will return a list of all the images in the folder.
For each image, you would need to make another connection to the server to retrieve the metadata for that image. This means that for every single image, you would need to make a separate call to the server, resulting in a total of 151 connections.
Here is a preview of the code for this scenario:
FirebaseStorage.getInstance().getReference().child("Images/").listAll().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (int i = 0; i < 150; i++) {
task.getResult().getItems().get(0).getMetadata().addOnCompleteListener(task1 -> {
if (task1.isSuccessful()) {
task1.getResult().getCustomMetadata("title"); //Finally I got the image title that stored inside image, it is cumbersome process.
} else {
//Handle error
}
});
}
} else {
//Handle error
}
});
However, it's possible that I may be missing something. Is this really the way it works, or is there a better solution? I don't want to name the image directly as the file name because I've tried that before and found that the length is not enough.
I would greatly appreciate any suggestions or alternative solutions to this problem. Thank you in advance!
Additionally, I would like to express my gratitude to OpenAI's GPT-3 for helping me write this clear and concise question.
First of all, FirebaseStorage.getInstance() is a singleton, which will always create a single instance.
Furthermore, while Mauricio Gracia Gutierrez's will work, please see below a solution that uses Tasks#whenAllSuccess(Collection> tasks):
StorageReference imagesRef = FirebaseStorage.getInstance().getReference().child("Images/");
imagesRef.listAll().addOnCompleteListener(new OnCompleteListener<ListResult>() {
#Override
public void onComplete(#NonNull Task<ListResult> task) {
if (task.isSuccessful()) {
List<StorageReference> storageReferenceList = task.getResult().getItems();
List<Task<StorageMetadata>> tasks = new ArrayList<>();
storageReferenceList.forEach(item ->
tasks.add(item.getMetadata())
);
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> objects) {
for (Object object : objects) {
String title = ((StorageMetadata) object).getCustomMetadata("title");
Log.d("TAG", title);
}
}
});
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
In this way, you'll wait until you have a List full of titles.
You can create a backend API method that returns the list of titles for you in a single call
I know now that your code was demostration purposes only but....
your code says task.getResult().getItems().get(0) meaning that only the first item is being used 150 times
Calling task.getResult().getItems() inside the for, means that you are calling something that only needs to be called once 150 times
Using harcoded 150 instead of the items.length is not a good idea
When you see that your code cumbersome or hard to read, follow the "Single Responsability" to split it into simpler methods.
In the code below I'm using type "Item" because I dont now the name of the type returned by task.getResult().getItems()
void getAllImagesMetadata() {
FirebaseStorage.getInstance().getReference().child("Images/").listAll().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Item []items = task.getResult().getItems() ;
for (int i = 0; i < items.length ; i++) {
title[i] = getTitle(items[i] ;
}
} else {
//Handle error
}
// Make sure the connection to get list of all items is closed here
}
string getTitle(Item item) {
string title ;
item.getMetadata().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
title = task.getResult().getCustomMetadata("title");
} else {
//Handle error
}
// Make sure the connection to get the metada is closed here
return title ;
}

Getting the guild owner in JDA raising NullPointerException

I was trying to make a function which displays info about the server.
public static void serverInfo(Guild guild, MessageChannel channel) {
EmbedBuilder embed = new EmbedBuilder();
//Calculations
int people = 0;
int roles = 0;
int tc = 0;
int vc = 0;
for (Member member : guild.getMembers()) {
if (!member.getUser().isBot())
++people;
}
for (Role ignored : guild.getRoles())
++roles;
for (TextChannel ignored : guild.getTextChannels())
++tc;
for (VoiceChannel ignored : guild.getVoiceChannels())
++vc;
String time = String.valueOf(guild.getTimeCreated());
String created = time.substring(8, 10) + "-" + time.substring(5, 7) + "-" + time.substring(0, 4);
embed.setTitle(guild.getName());
embed.setThumbnail(guild.getIconUrl());
embed.addField("Total Members", String.valueOf(guild.getMemberCount()+1), true);
embed.addField("Members", String.valueOf(people),true);
embed.addField("Bots", String.valueOf((guild.getMemberCount()+1)-people), true);
embed.addField("Owner", Objects.requireNonNull(guild.getOwner()).getUser().getName(), true);
embed.addField("Roles", String.valueOf(roles), true);
embed.addField("Text Channels", String.valueOf(tc), false);
embed.addField("Voice Channels", String.valueOf(vc), true);
embed.addField("Date Created", created, false);
channel.sendMessageEmbeds(embed.build()).queue();
}
However, this raises a NullPointerException
java.lang.NullPointerException at
java.base/java.util.Objects.requireNonNull(Objects.java:208) at
com.television.Commands.Infos.serverInfo(Infos.java:38) at
com.television.CommandExecutor.onMessageReceived(CommandExecutor.java:19)
But, if I removed this part from the function, it works just fine, and no exception is raised.
for (Member member : guild.getMembers()) {
if (!member.getUser().isBot())
++people;
}
Why does this happen? This problem also gets raised only in 1 server, out of the 3 servers I've tested in.
And, secondly, I know this is not much related to the question from the title, how can I calculate the number of members/bots because this part (the for-each loop in the code snippet above) does not calculate the number of members correctly, it always has 1 as the value of the bot variable, and therefore number of members - 1 is the value of people.
Two things in advance: You have to either cache all the members from every guild or instead (recommended) retrieve them when needed. To be able to do so, you need to enable the GUILD_MEMBERS Privileged Intent.
You can pretty easily retrieve a List representing all members of a guild with the following method:
public CompletableFuture<List<Member>> loadMembersFull(Guild guild) {
CompletableFuture<List<Member>> future = new CompletableFuture<>();
if (guild.isLoaded()) {
future.complete(guild.getMembers());
} else {
guild.loadMembers()
.onError(future::completeExceptionally)
.onSuccess(future::complete);
}
}
With that you can then move on with all your other stuff.
I actually don't know why it would work without the for-loop, but it looks like the error does not occur there, but when loading the owner, as it throws the exception in your #requireNonNull.
The owner object is null when he/she is no longer in the guild or not yet loaded. The owner could also have deleted the account or get banned by Discord.
To also solve this problem, I recommend you to replace your line with the following one:
embed.addField("Owner", Optional.ofNullable(guild.getOwner()).map(owner -> owner.getUser().getName()).orElse("<not found>"), true);
To get the proper amount of users, you should filter the list of users for whether they are bots or not.
int amount = (int) loadMembersFull(guild).join().stream()
.map(Member::getUser)
.filter(user -> !user.isBot())
.count();
If you need more help, feel free to ask me on my Discord server

Teleport to next player

I am working on a Spigot 1.8.9 plugin and am trying to add a feature when a staff right-clicks an item it teleports them to the next player that isn't in vanish and not themselves and if there aren't any it should return null.
On click I attempted to add all possible users to a list using
public static List<User> getPossibleUsers(User user){
List<User> result = new ArrayList<>();
for(User target : users)
if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
result.add(target);
return result;
}
The staff is also assigned an int called nextPlayer which is set to 0 when they login. Then when they click I add one to the int so next time they click it can get the next user.
private User getNextPlayer(User user) {
int next = user.nextPlayer;
List<User> users = getPossibleUsers(user);
if(users.size() == 0)
return null;
int current = 0;
for(User target : users) {
if(current == next){
return target;
}
current++;
}
user.nextPlayer = next;
}
The problem is I don't know how to make the getNextPlayer method correctly and make it efficient. I also would like to also to make it so once it hits the last player it loops back to the first player.
I'd suggest thinking about your problem entirely differently if you want it to be efficient, but efficiency really isn't a concern in this situation, so I'm opting to not pre-maturely optimize and instead work with the code you already have.
public static List<User> getPossibleUsers(User user){
List<User> result = new ArrayList<>();
for(User target : users)
if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
result.add(target);
return result;
}
This currently returns the Users in the same order, as they are defined on users.
This better have a natural sort order, otherwise you are going to have issues when people join / leave the server, as it will cause people to change their ordering in the list.
Now let's get back to first principals.
int next = user.nextPlayer;
Looks like you are storing the index of the player in the list you have already been in on the 'user'.
Once you have this, you can access that index directly from the list.
https://docs.oracle.com/javase/8/docs/api/java/util/List.html#get-int-
E get(int index)
So, doing users.get(next++); is all you need to do to 'fix' the code you have above. it increments next, and gets the user at that position (assuming the ordering is consistent, and hasn't changed) However, it may throw an exception if it's out of range of the list, so we wrap it in
if(next <= users.length) {
users.get(next++);
} else return null;
This will change it to returning null, if it would otherwise throw an exception.
BUT all of this still has a fatal flaw, that if the list is mutated between calls, that you could be potentially skipping or changing the order around.
A far better solution to this, is to instead cache the visited users, as well as the last visited user.
If the users are ordered, and you store the last visited user, instead of the index, you are storing data that is much more resilient to change, and more closely matches the behavior you want.
To more closely match your needs, you are asking that.
Generate a predictable, ordered list of users that don't include the admin, or anyone else that is vanished, to aid the admin in predicting where they are going.
Rotate through this list, by right clicking with a tool, (Note this is async, so all the state needs to be saved)
Ensure that all visited users are visited before repeating the sequence.
public class TeleportTooldata {
private ListIterator<UUID> cursor;
private List<UUID> cachedOrder;
public TeleportTooldata(List<UUID> applicableUsers) {
cachedOrder = applicableUsers;
}
#Nullable
public UUID next() {
if (!cursor.hasNext()) return null;
UUID next = cursor.next();
if (!cachedOrder.contains(next)) {
cachedOrder.add(next);
}
return next;
}
public void Update(List<UUID> applicableUsers) {
applicableUsers.removeAll(cachedOrder);
cachedOrder.addAll(applicableUsers);
}
}
public class TeleportToolUtil {
YourPluginUserRepo repo;
Map<User, TeleportTooldata> storage; //This could be a cache, make sure to remove if they log out, or maybe timed as well.
public List<UUID> getApplicableUsers() {
return repo.getOnlineUsers().stream()
.filter(User::isVanish)
.sorted(Comparator.comparing(User::getId)) // You can change the sort order
.map(User::getId)
.collect(Collectors.toList());
}
public void onToolUse(User user) {
TeleportTooldata data = storage.computeIfAbsent(user, x -> new TeleportTooldata(getApplicableUsers()));
UUID next = data.next();
if (next == null) {
data.Update(getApplicableUsers());
next = data.next();
if(next == null) {
storage.put(user, new TeleportTooldata(getApplicableUsers()));
next = data.next();
}
}
user.teleportTo(next);
}
}
A few changes.
We are now caching the ordering, so that you could conceptually also let the user go backwards through the list.
We are using ListIterator. ListIterator is an object that loops through lists, and stores the current position for you! Much like you were doing before, but without indexes.
We now have the possibility to update the data, in case a player joins late, or someone unvanishes they will be put at the back of the list if they are not already inside it.
when we run out of users, we attempt an update, if we are really out, we start again with a brand new list. (note this won't guarantee the same order every time (people will be 'properly' sorted when it updates if they were previously appended, but it's close enough for this usecase)
However! We still need to be mindful of memory leaks. using UUID's rather then players or users, means this class is very light weight, we should be pretty safe from memory leaks in the list of UUID AS LONG as the TeleportTooldata doesn't live too long.
You can replace the Map of TeleportTooldata with a cache (maybe from Guava?) to remove the data some time after the admin leaves the game.
If TeleportTooldata was expected to be long-lived, we would want to seriously consider removing UUID's from the history.
Also, not handled in my example, is the possibility of the users going offline after the order is cached.
To handle this, before teleporting the player, check if the uuid is online, otherwise go to the 'next' and follow all the same logic again.

Excatly how to test? (Unittest, JUnit, PhpUnit)

Well this question might be too localized.
Lets suppose I have forum system to test. Lets delete an user and his posts. Let me use a pseudo-code for the sake of simplificity:
class User
{
function add() { ... }
function delete (userID)
{
container::getOrCreateUserPostObject.deletePostsByUserID (userID)
DELETE FROM users WHERE ID = userID
}
}
class UserPost
{
function deletePostsByUserID (userID)
{
DELETE FROM posts WHERE USERID = userID
}
}
this now must be tested:
function testDeleteUser()
{
container::getOrCreateUserObject.add();
container::getOrCreateUserObject.add();
container::getOrCreateUserObject.delete (1)
// now check in the DB that how many records left, really one was deleted etc.
}
another test
function testDeletePosts
{
container::getOrCreateUserPostObject.deletePostsByUserID (1);
// again, now check in the DB that how many records left, really one was deleted etc.
}
this looks OK so far. The user deletion and user posts deletion works, and their test standalone.
Yes, standalone. We checked if its OK to delete an user and checked if its OK to delete his post. We didnt check if we delete an user with his posts works! There are two good working "lego" elements but is that OK if we put them together?
If I put this "global" test to testDeleteUser() then I repeat the post-deletion test code...
I don't know if i get you right, but in a test, you should not really rely on specific user id's like you are doing in testDeletePosts(), you should rather add a user here as well, add some posts, and delete these posts again. So your test is completely independent.
Update:
Something like this for checking the referential integrity
function testDeleteUsersAndPosts
{
addedUsers[0] = user.add();
addedPosts[0] = post.add(addedUsers[0], 'first Post')
addedPosts[1] = post.add(addedUsers[0], 'second Post')
addedUsers[1] = user.add();
addedPosts[2] = post.add(addedUsers[1], 'third Post for the second user')
// Check how many posts you have
allPosts = post.get().count()
for (id in addedUsers)
{
user.delete(id)
}
// Check how many posts you have now
allPostsNow = post.get().count();
return allPostsNow == (allPosts -3)
}
And something like this for checking the Post deletion only
function testDeletePosts
{
userID = user.add();
addedPost = post.add(userID, 'first Post')
// Check how many posts you have
allPosts = post.get().count()
post.delete(addedPost)
return post.get(addedPost) == false
}

Java Group Chat Application: Knowing if groups have users currently active

For the past 2 weeks, I have been working on a java group chat application. So far a user can login and join groups created by other users. The user can also send chat messages to each other inside of each group. The message distribution is handled by a server and the server only sends the chat messages to clients of the group from which it was sent. A list of groups which the user is a part of is also displayed, and I would like for there to be a small green mark(or anything else to notify the user) to let the user know if there are other users currently active on that chat room group.
Here is how my groups are stored in the database
**TABLE groups**
- id
- user_count
**TABLE group_members**
- groupid
- userid
The sending and receiving of chat messages are handled using sockets, and none of the sent messages are stored on the database. I was thinking of maybe having a field in the groups table where every time a user joins would increment the value in it by 1 and every time a user leaves, the value goes down by 1. This way if the number stored in this field is 0, there are no current users on here. I think there may be a better way to handle this though.
You could have an arraylist for every group that holds the names of every player in that group. Then you could easily get the names of people in the group and the number of people in it. so if you have a class named Group you could have something like:
class Group {
private ArrayList<String> users; // or instead of String if you have a User class use that
public Group(Type par) {
users = new ArrayList<String>();
}
public static void addUser(String userName) {
users.add(userName);
updateUsers();
}
public static void removeUser(String userName) {
for(int i = 0; i < users.size(); i++) {
if(users.get(i).equalsIgnoreCase(userName)) {
users.remove(i);
}
}
updateUsers();
}
public static int getNumberOfUsers() {
return users.size();
}
public static void updateUsers() {
for(String e : users) {
// send the user the necessary info to all users in the group
}
}
}
I just went with my idea as stated in the question. Works well and fast enough for my needs. heres how the database looks
TABLE groups
id
user_count
activity <--- this is where the values are increased or decresed.
TABLE group_members
groupid
userid

Categories

Resources