Generate Unique Object ID - java

I'm trying to generate a new unique identifier for each object in a class, without using a static nextID field to just increment. Using that will create complications when unloading and unloading the program.
I came up with a solution to loop through the objects in a static method checking them all but for some unknown reason to me, it won't exit the while-loop.
I have been testing it in ideone.com here trying to create 5 objects to begin with, though it won't even create one.
Without having to go to the link to view the whole testing code, below is the method I'm using.
public static int newRandomID() {
int randomID = 0;
boolean notUnique = true;
while (notUnique) {
randomID = (int) (Math.random() * 999999 + 1);
for (Example e : examples) {
if (e.ID == randomID) {
notUnique = true;
break;
}
notUnique = false;
}
}
return randomID;
}
Have I just made a stupid mistake that I'm too blind to see, or is there a reason that this isn't working?

If all you need is a unique identifier (that need not be sequential) and it dosn't have to be an integer, have a look at java.util.UUID

your notUnique is bit confusing and i think you are doing it wrong in here
if (e.ID == randomID) {
notUnique = true;
break;
}
you dont need to break the statement if the id exists. i changed you code may be this helps.
int randomID = 0;
boolean ContinueLoop = true;
while (ContinueLoop) {
randomID = (int) (Math.random() * 999999 + 1);
boolean exist = false;
for (Example e : examples) {
if (e.ID == randomID) {
exist = true;
}else{
exist = false;
break;
}
}
if(exist==false){
ContinueLoop = false;
}else{
ContinueLoop = true;
}
}
return randomID;

Tried to execute your code (from the link you sent):
After you created and printed 50 new ids, I tried to generate 150,000 more:
for (int i = 0; i < 150000; i++)
new Example();
and... it works perfectly fine! Just took it a minute or so (which makes sense).
If I try to create only 15,000 records it works in less than a second.
Which leads me to the conclusion that miss rate is exponentially high and starts to be unbearable once you reach 15% of the ids capacity.
Don't continue with this solution.
Use a different approach such as a stored sequential number (if you store the records). If you don't store the records, I don't see a reason why not to use a static int variable.

Related

How can I change how an objects field is PRESENTED while still retaining the original value of said field?

I'm working on a project that involves creating a Spring Boot REST Application using JDBC Template to access a database of my own creation (MySQL). I'm using Postman to verify endpoints and entering data using JSON through postman. I'm currently tasked with creating a guessing game that generates a random 4 digit number, easy enough. My issue is this; " Returns a specific game based on ID. Be sure in-progress games do not display their answer" I've created a "starter" method that fulfills the requirement, but only at it's most basic level. I cannot operate on an object once it's field's value is changed to "Hidden" as it is no longer a 4 digit number. I'd like to hide the answer while still being able to operate in a functional manner on the randomly generate number. Here is my base method for this return:
#Override
public Game gameById(int id) {
Game game = gameDao.getGameById(id);
if (game.getFinished() == false) { // "Hides" the answer
game.setGameAnswer("Hidden");
}
return game;
}
Here is my method that uses the above code in my application:
#PostMapping("/guess")
public ResponseEntity<Game> play( int id, String guess) {
Game game = service.gameById(id); // Get method
if (game == null) {
return new ResponseEntity(null, HttpStatus.NOT_FOUND);
}
Round round = new Round();
round.setGuess(guess);
service.guess(round, game);
return ResponseEntity.ok(game);
}
Any ideas?
EDIT:
Game Object:
public class Game {
int gameId;
String gameAnswer;
Boolean finished;
List<Round> Rounds = new ArrayList<>();
I will also include the database game table which i am storing said fields into:
CREATE TABLE game(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
gameAnswer CHAR(4) NOT NULL,
finished BOOLEAN DEFAULT false
);
To clarify my question, when running the game in postman, the game object and it's fields are all displayed as well as the games answer. My goal is to hide this answer as long as the games status reads "false". Once the number has been correctly guessed the status of the game will automatically be set to true.
Here is an example from the JSON output of postman:
{
"gameId": 1,
"gameAnswer": "2651",
"finished": false,
"rounds": [
{
"roundId": 1,
"guess": "1234",
"timeOfGuess": "2021-06-18 09:28:29",
"exactMatch": 0,
"partialMatch": 0,
"gameId": 1
}
]
}
While the "Finished" is set to false, I want the game answer to read "Hidden", but still have the numerical value be present to operate on.
Guess Method:
#Override
public void guess(Round round, Game game) {
// Takes the games generated answer and the user's guess and breaks them down into character array's
round.setGameId(game.getGameId());
char[] gameAnswer = characterBreakDown(game.getGameAnswer());
char[] roundGuess = characterBreakDown(round.getGuess());
// used for comparing the array elements one by one
int min = 0;
int max = 1;
int exact = 0;
boolean run = true;
while (run) {
for (int i = 0; i <= 3; i++) {
if (Arrays.equals(roundGuess, gameAnswer)) {
round.setExactMatch(4);
game.setFinished(true);
gameDao.updateGameById(game);
run = false;
} else if (Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
exact++;
round.setExactMatch(exact);
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
} else if (!Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
}
}
}
game.getRounds().add(round);
roundDao.createRound(round);
}
If I understand correctly, your issue is that in one use case (when you are responding to an API request), you want to retrieve a Game and respond to the API request with the Game's "gameAnswer" field hidden, but for other, purely background operations, you want to retrieve the Game with the "gameAnswer" field intact.
Assuming that's the case, I think that your only real issue is where you "blank-out" the "gameAnswer" field.
It looks like you are currently doing that in the "gameById" method of the repository. That results in the gameAnswer being hidden in all cases, right?
So, the solution would be to blank-out the gameAnswer in the controller method instead.
If I have this wrong / backwards I apologize, but the way that you posted your code, in pieces, makes it hard to follow. But anyway, I believe that the solution to your problem is to only blank-out the field where it actually needs to be blanked out. Or if you are having some other issue with it being blanked out / not blanked out because you are doing so in some shared method, perhaps create a new method for the use-case that needs it the other way.

Random number guessing game with limitations after each guess

I am making a number guessing game:
The computer generates a number inside an interval
I try to guess it and receive a reply whether it's higher/lower than my guess or equals to my guess and I've won
There is an interval in which I can guess, as well as a guess attempt limit
The trick is, however, that I need to implement another condition: each guess should "shrink" the interval in which I'm able to guess. For example: computer generates 50, I guess 25, computer replies "The random number is larger.". Now knowing that, I should not guess anything lower than 25 again, it's unreasonable. In case I guess i.e. 15, the computer should reply "The guess doesn't make sense.". I understand that I somehow need to save each guess value to a new variable, but nothing seems to work. I'm a beginner, please bear with the following code, I've tried a lot of things:
public String guess(int guess)
{
int lowerBound = 0;
int upperBound = 99;
Set<Integer> lowerGuesses = new TreeSet<>();
Set<Integer> higherGuesses = new TreeSet<>();
if (gameOver) {
return "The game is over.";
}
if (guess < 0 || guess > 99) {
return "The guess is out of bounds.";
}
if (guessCount < maxGuessCount) {
if (guess < secretNumber) {
if (lowerGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
else {
guessCount++;
lowerBound = guess;
lowerGuesses.add(guess);
return "The random number is larger.";
}
}
if (guess > secretNumber) {
if (higherGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
else {
guessCount++;
upperBound = guess;
higherGuesses.add(guess);
return "The random number is smaller.";
}
}
if (lowerGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
if (higherGuesses.contains(guess)) {
return "The guess doesn't make sense.";
}
}
if (guess < lowerBound || guess > upperBound) {
return "The guess doesn't make sense.";
}
if (guessCount == maxGuessCount) {
gameOver = true;
victorious = false;
return "Ran out of guess attempts.";
}
guessCount++;
gameOver = true;
victorious = true;
return "You won.";
}
Thank you in advance!
First, to avoid confusion, let's rename the method in order to make sure that its name is not an exact match with its parameter, so this is how it should look like:
public String makeGuess(int guess)
avoid naming different entities in the same name space with the exact same name (local variables being present in different methods or parameters having similar names with data members for the purpose of initialization are an exception). From now on, you will call the method as makeGuess(25), for example.
Now, to the actual problem. You have an incorrect assumption. You assume that you need to keep track of past intervals. That's not the case. You can just change the edges of the intervals. Also, your code is superfluous, I advise you to refactor it. Finally, you always initialize upper bounds, local bounds and higher and lower guesses as local variables, so they will never be kept track of. Instead of this, you need to perform the following simple measures in order to make this work:
Define the bounds and limit as data members
protected int lowerBound = 0;
protected int higherBound = 99;
protected int lb = 0;
protected int hb = 99;
protected int limit = 5;
protected int guessCount = 0;
protected int randomizedNumber; //Initialize this somewhere
Note that I have hard-coded some values. You might want to make this dynamic with initialization and stuff like that, but that's outside the scope of the answer. lowerBound, higherBound, limit are game settings. while lb, hb, guessCount represent the game state. You could separate this logic into another class, but for the sake of simplicity, even though I would program differently, I will leave them here in this case.
Have a method that initializes the game
public void initialize() {
lb = lowerBound;
hb = higherBound;
guessCount = 0;
}
So you separate your concern of game initialization from the outer logic of starting and maintaining a game.
Implement makeGuess in a simplistic way
public String makeGuess(int guess) {
if (++guessCount >= limit) return "The game is over.";
else if ((lb > guess) || (hb < guess)) return "The guess doesn't make sense";
else if (randomizedNumber == guess) return "You won.";
else if (guess < randomizedNumber) {
hb = guess;
return "The random number is smaller.";
} else {
lb = guess;
return "The random number is larger.";
}
}
NOTE: I dislike mixing up the logic with the output layer, the reason I did it in the method above was that you have mentioned you are a beginner and my intention is to make this answer understandable for the person who just begun programming and is very confused. For the purpose of actual solutions, you should return a state and in a different layer process that state and perform the console/UI operations you need. I will not go through the details now, as it would also be outside of scope, but for now, please have some success with the solution above, but THEN you should DEFINITELY look into how you need to code, because that is almost as important as making your code work.

Random Number Occurrence Check

I have an array of Strings and when the user taps the button inside my app I generate a random number and use it to select a random String from my facts[] array. However, I tried improving my code so that the same random number would "never" occur(leading to the same String been shown to the user). Despite my efforts, my "Check" blocks doesn't seem to work since it generates a random fact when I click the button for the first time and then it does nothing. Please help me figure out the correct logic behind this and maybe write a more efficient code-block.
My current logic: check if the random number that has been generated already exists in my int[] factsCheck array and if it does create another one.If it doesn't add it to the array so that the program knows it has already been created once.
int[] factsCheck = new int[facts.length];
boolean isNotNewRandomNumber = true;
int count = 0;
int randomNumberToReturn;
private void initFactsCheck() {
for(int i=0; i<=factsCheck.length;i++) {
factsCheck[i] = -1;
}
}
String getFact() {
// Randomly select a fact
Random randomGenerator = new Random();
while(isNotNewRandomNumber) {
randomNumberToReturn = randomGenerator.nextInt(facts.length);
for(int i = 0; i<factsCheck.length; i++) {
if(factsCheck[i] == randomNumberToReturn) {
break;
} else {
count++;
}
}
if (count == factsCheck.length) {
// Doesn't exist
isNotNewRandomNumber = false;
}
count = 0;
}
return facts[randomNumberToReturn];
}
In the beginning of getFacts(), add this:
isNotNewRandomNumber = true;
The problem is that you isNotNewRandomNumber to false the first time you call getFacts(), then you never set it to true again, so you will never go into the while loop again.
I'm not sure that's all you need to do. There might be other errors too. It seems unnecessary to have a for loop inside the while loop. There must be a better way. And you probably want to set factsCheck[x] to some appropriate value just before the return statement.

How would I ask java to stop until a variable is changed?

so I am trying to design a GUI with the program BlueJ, that sends data from a jtextfield box into a variable (already done), and using that variable to be able to update another variable, but for java to "stop running" until a specific variable is updated. So something along the lines of...
string bacon = "";
int agility = 1;
int dexterity = 2;
int strength = 3;
int intelligence = 4;
int charisma = 5;
//my variables.
if (bacon = "agility")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
agility= agility+bacon
}
else if (bacon = "dexterity")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
dexterity = dexterity+bacon
}
else if (bacon = "strength")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
strength = strength+bacon
}
else if (bacon = "intelligence")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
intelligence = intelligence+bacon
}
else if (bacon = "charisma")
{
//what I am doing goes below where words are being used instead
Stop java progression until bacon is updated with an integer.
charisma = charisma+bacon
}
Thank you very much to anybody who can help me figure this out. I would also like it to have something so that if bacon is stated as a non-integer (32.7 or "hello"), it would simply ask you to input a proper integer.
Not quite sure what you are asking in the first part of the question, but for the second part to it check if it is a non integer you can do something like this....
boolean isValidInput = true;
for(int i=0;i<bacon.length();i++) {
char charAt = bacon.charAt(i);
if(!Character.isDigit(charAt)) {
isValidInput = false;
break;
}
}
if(!isValidInput)
System.out.println("Invalid Input!");
Also, = is used for assignment in java, ex a = 3;, however if you are trying to check if something is equal to something else, you should use the == operator. ex. if(x==2)
But in your case, since you are comparing Strings, you should use if(x.equals("hello"))
Another tip, instead of saying charisma = charisma + bacon; you can just say charisma += bacon; as a shorthand ;)
Hope this helps,
Saashin

Code clean up for if else statement

Here is the peace of code, Which I am looking to make it more efficient
I see lots of if else statements,
Can any one help me to make it simple and reduce the number of lines of code
if(mmPageCounter.equalsIgnoreCase("trueNotFull")){
if(whatPageNext.equalsIgnoreCase("first"))
accountListSize = 5;
else
accountListSize = acctIdList.size();
}else if(mmPageCounter.equalsIgnoreCase("true")){
if(whatPageNext.equalsIgnoreCase("first"))
accountListSize = 5;
else
accountListSize = 10;
}else if(mmPageCounter.equalsIgnoreCase("false")){
if(whatPageNext.equalsIgnoreCase("alwaysFirst"))
accountListSize = acctIdList.size();
}
In the above code based on the mmPageCounter based on the value I am setting the
index of the For loop either to the full capactiy, or to the actual value
Full capacity means, The for loop will iterate only 10 items
ps : I have got the code working and its fine, But i am looking only for the fine tuning
If any one one has any fine tuning tips please share the links.
I would write it reversely.
When account size should be set to 10?
When account size should be set to 5?
if(shouldBeFive()){
accountListSize = 5;
} else if (shouldBeTen()){
accountListSize = 10;
} else{
accountListSize = acctIdList.size()
}
Also you can switch-case in java7.
This does not make it faster but reduces code, you can substitute:
if(whatPageNext.equalsIgnoreCase("first"))
accountListSize = 5;
else
accountListSize = 10;
by:
accountListSize = whatPageNext.equalsIgnoreCase("first") ? 5 : 10;
and:
if(whatPageNext.equalsIgnoreCase("first"))
accountListSize = 5;
else
accountListSize = acctIdList.size();
by:
accountListSize = whatPageNext.equalsIgnoreCase("first") ? 5 : acctIdList.size();
UPDATE:
If mmPageCounter can only be "trueNotNull", "true", or "false" then I think this code is equivalent:
int maxsize = mmPageCounter.equalsIgnoreCase("true") ? 10 : acctIdList.size();
if(mmPageCounter.equalsIgnoreCase("false")){
if(whatPageNext.equalsIgnoreCase("alwaysFirst"))
accountListSize = accountListSize = acctIdList.size();
} else {
accountListSize = whatPageNext.equalsIgnoreCase("first") ? 5 : maxsize
}
but you should write tests against current code and check that this code behaves exactly in the same way on edge cases.
My suggestion is not to make your code shorter, but more extensible and easier to maintain. It will however make the client code a lot shorter.
I would choose an enum but without a switch statement, as I can spot a strategy here and I'd prefer to encapsulate the logic outside the client. Also, I'd provide an abstraction (an interface) that would allow me or others to come with alternative implementations in the future, without adding new constants to the enum. The literal values for your strings and numbers I'd define as constants (in the abstraction, if that makes sense)
Here's the "strategy" abstraction:
interface PageCounter {
String FIRST = "first";
String ALWAYS_FIRST = "alwaysFirst";
int FIRST_SIZE = 5;
int DEFAULT_SIZE = 10;
int getAccountListSize(String whatPageNext, List<?> acctIdList);
}
Here's the enum implementing the PageCounter interface and providing the default implementations:
enum DefaultPageCounter implements PageCounter {
TRUENOTFULL {
#Override
public int getAccountListSize(String whatPageNext, List<?> acctIdList) {
return FIRST.equalsIgnoreCase(whatPageNext) ? FIRST_SIZE : acctIdList.size();
}
},
TRUE {
#Override
public int getAccountListSize(String whatPageNext, List<?> acctIdList) {
return FIRST.equalsIgnoreCase(whatPageNext) ? FIRST_SIZE : DEFAULT_SIZE;
}
},
FALSE {
#Override
public int getAccountListSize(String whatPageNext, List<?> acctIdList) {
// some default (non)value is required here: -1 can be whatever.
return ALWAYS_FIRST.equalsIgnoreCase(whatPageNext) ? acctIdList.size() : -1;
}
};
}
Here's your code (a lot shorter in the client, and unaware of the existing possibilities)
PageCounter counter = DefaultPageCounter.valueOf(mmPageCounter.toUpperCase());
int accountListSize = counter.getAccountListSize(whatPageNext, acctIdList);
The counter can be provided by some factory method so that you can switch the PageCounter implementation for subclasses (that's why I chose to use an interface after all), but I didn't want to make this more complicated than it already is.
If you know all possible values of mmPageCounter and whatPageNext beforehand, you can use a HashMap.
During initialization:
accountListSizes = new HashMap<String, Integer>();
accountListSizes.put("trueNotFull|first", 5);
accountListSizes.put("true|first", 5);
...
During computation:
String key = String.format("%s|%s", mmPageCounter, whatPageNext);
Integer value = accountListSizes.get(key);
if (value == null) {
accountListSize = acctIdList.size();
} else {
accountListSize = value;
}
This implementation is a little bit slower than yours, but it is much easier to read and extend. You can alter the mapping even at run-time.

Categories

Resources