my class has a method as follow :
private volatile String currentSeqSecondBucket = "";
private volatile int currentUriSeq = 0;
public String calculateRepositoryURI(Date now, String userSpecifiedFolder)
{
String folder = userSpecifiedFolder;
if (StringUtils.isBlank(folder))
folder = RSIConstant.CONTENT_ROOT_FOLDER;
StringBuilder buf = new StringBuilder(folder);
if (!folder.endsWith("/"))
buf.append("/");
String nowStr = YYYYMMDDHHMMSS_FORMAT.format(now);
buf.append(String.valueOf((int)(Math.random() * 1000)));
buf.append("/");
buf.append(nowStr);
buf.append("_");
synchronized (this)// here the this means my class
{
if (currentSeqSecondBucket.equals(nowStr))
{
currentUriSeq++;
}
else
{
currentUriSeq = 1;
currentSeqSecondBucket = nowStr;
}
buf.append(String.valueOf(currentUriSeq));
}
buf.append(".xml");
return buf.toString();
}
Will this code return a unique string for every call?
Will this code return a unique string for every call?
No. It won't. But the reason is nothing to do with thread safety.
The reason is that occasionally you may get the same random number generated twice in the same second. Now if the same random number was generated twice in a row, then your test currentSeqSecondBucket.equals(nextStr) would catch this and you would increment currentUriSeq and avoid the collision. However, a more likely scenario is that the random number repeats after more than one call in the same second. That will defeat your test.
My advice would be to not try to re-invent the wheel:
If you want non-guessable unique names, use the UUID class to give you uniqueness.
If you don't care about guessability, just use a simple sequence number; e.g. implemented using AtomicInteger or AtomicLong.
Related
For my current java project, I am trying to generate random ID's for registered users. So far I have been using min +(int) (Math.random()*((max-min)+1)) as my formula to generate the random number. The problem that I am facing is that sometimes the numbers repeat themselves and my application wouldn't work with them.
int min = 1001;
int max = 1050;
for (int i=1; i<=1; i++)
{
int a = min +(int) (Math.random()*((max-min)+1));
}
I have tried using and incorporating
Integer[] arr = new Integer[100];
for (int i = 1; i < arr.length; i++) {
arr[i] = i;
}
Collections.shuffle(Arrays.asList(arr));
but numbers generated would constantly come out as "null" and it would repeat the loop a few hundred times and flood my txt file.
In general, random generators Random or Math.random() are not the correct ways to generate a unique id. As you mentioned, it could be repeated (and it will definitely be).
I would recommend two ways of generating ID.
The first one is to use AtomicInteger. This is good when your ID should be unique but not random.
private static final AtomicInteger ID = new AtomicInteger(0);
public static String generateUniqueId() {
return String.valueOf(ID.incrementAndGet());
}
The second one, which is preferable to me, is to use UUID. This is good when your ID should be as unique as random.
public static String generateUniqueId() {
return String.valueOf(UUID.randomUUID());
}
Another one, I can mention is to use System.nanoTime().
public static String generateUniqueId() {
return String.valueOf(System.nanoTime());
}
Long ago I had some investigation and find out that this is pretty stable for normal payload. But in general, it could retrieve the same value if you build such a system, that should generate ID so often.
Instead of generating numbers I would recommend to generate UUID. The chance of a is collision is close to impossible.
UUID id = UUID.randomUUID();
Otherwise if you want to stick with numbers I would recommend you to implement yourself some Sequence service within your application.
import java.util.concurrent.atomic.AtomicLong;
public class SequenceService {
private final AtomicLong ids;
public SequenceService() {
long initialValue = getInitialValue();
this.ids = new AtomicLong(initialValue);
}
public long generateNextId() {
return ids.incrementAndGet();
}
private long getInitialValue() {
// this methods reads the last known leased id (e.g. from the file system)
}
}
Im having an issue where I have to create a method in java to get a ticket number which every time it is called it generates a number sequentially.
this is what I have so far regarding the ticket method
public class Ticket
{
public static final String PREFIX = "CAR";
public static int number = 1000;
//instance variables
private String ticketNumber;
public Ticket(){
ticketNumber = generateTicketNumber();
}
public String getTicketNumber(){
return ticketNumber;
}
private String generateTicketNumber(){
number = number++;
ticketNumber = PREFIX +number;
return ticketNumber;
}
I'm told to use a static variable (which i have) to create and hold a counter to generate part of the ticket number, increment the static variable and assign it combined with the string prefix to the field ticketNumber. When i create an object it does not increment to CAR1001, it just goes CAR1000, am I to try a while loop for this?
number = number++; is not evaluated how you think. What this actually does is that the right-hand side increments number, but number++ is also an expression whose value is the old value of number, before the increment is done. Then because of the number = ... assignment, that old value is assigned to number on the left-hand side, undoing the increment.
So you should just write number++; instead, which simply increments the variable.
Unless I'm missing something, I think you've over complicated this. All you need is PREFIX and number. Concatenate PREFIX with number and increment number. That can be done in one step like,
public class Ticket {
private static final String PREFIX = "CAR";
private static int number = 1000;
public String getTicketNumber() {
return PREFIX + number++;
}
}
Or, perhaps a little easier to read,
public String getTicketNumber() {
try {
return PREFIX + number;
} finally {
number++;
}
}
I have a problem with my class variables, as always ^^
So I'm constructing a class named Prng, with variables
private int randListSize = 10;
private byte randList[] = new byte[randListSize];
private byte[] seed = new byte[]{ 34, -70, -4, 117, 98 };
the getters/setters associated
and the method
public void prng() {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(seed);
random.nextBytes(randList);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
in another class named Test.java, I want to :
1) set randListSize to /number of random bytes I want
2) have the randList of this size, and not from original 10 size
whenever I try, my randList is always of size 10. Can you help me please ?
in my class Test I've written :
Prng prng = new Prng();
System.out.println(prng.getRandListSize() + " " + prng.getRandList().length);
prng.setRandListSize(11);
System.out.println(prng.getRandListSize()+ " " + prng.getRandList().length);
which returns me "10 10 ; 11 10" and I want "11 11" at the end.
EDIT : here's my getters/setters :
public int getRandListSize() {
return randListSize;
}
public void setRandListSize(int randListSize) {
this.randListSize = randListSize;
}
public byte[] getSeed() {
return seed;
}
public void setSeed(byte[] seed) {
this.seed = seed;
}
public byte[] getRandList() {
return randList;
}
public void setRandList(byte[] randList) {
this.randList = randList;
}
First, randListSize, in my opinion, is a useless field, as that property can be retrieved directly from the array, and as the operation isn't expensive the value doesn't need to be cached. Thus, you really don't need getters/setters for that field either. I see you're using it as an initial size variable, but in that case I think it'd be better for it to be a parameter for a constructor/factory method instead, as it really doesn't need to be used anywhere else.
Second, setRandListSize() doesn't actually change randList's size, as arrays, once created, cannot be structurally modified (i.e. you can't make arrays longer/shorter after creating them). You're just changing an unrelated variable, which leads to some confusion once randListSize stops matching randList.length. This is the reason you're seeing 11 10 instead of 11 11 -- randListSize is only used at the moment of array creation, and later changes to randListSize don't affect the array.
In order to get the result you want, you're going to have to allocate an entirely new array and set randList to point to it instead of your old one, which you can do using your setRandList() method. Alternatively, you can write a method, perhaps called createNewRandList(int newLength), to do all the work at once.
Your setRandListSize method will need to recreate the randList array. If you need to keep the data in it, your method should copy whatever data can fit into the new array.
public void setRandListSize(int randListSize) {
this.randListSize = randListSize;
this.randList = new byte[randListSize];
}
In a interview I was asked to wrtie a method which will generate unique 5 digit random number everytime when it is called.For ex: if I call the method and get 22222 then in next call i should not get 22222.
I wrote a code as below:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class RandomNumberGen {
private static ArrayList arr=new ArrayList();
private static int k=-1;
public RandomNumberGen(){
for (int i=10000;i<99999;i++){
arr.add(i);
}
Collections.shuffle(arr);
}
public static void main(String[] args) {
for(int m=0;m<10;m++){
try {
System.out.println(new RandomNumberGen().randomNumbermethod());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Integer randomNumbermethod() throws Exception{
k++;
if(k>=arr.size()){
throw new Exception("No more number available");
}else return (Integer) arr.get(k);
}
}
Answer got accepted but I was asked to avoid memory wastage now.
My question is here as you can see I am using only 10 numbers.So rest of the space occupied by arraylist is a memory-wastage.Is there a way I can achieve same thing without using extra memory.
What I mean is there someway using which unique number can be generated on each call so that this much memory do not get wasted.
private static int number = 10000;
public int getNextUniqueRandomNumber() {
return number++;
}
Implications:
In order to not to return the same value twice, you need to track which numbers you've already generated. This can be very memory consuming.
You eventually run out of numbers. Instead of keeping on searching for an unused random number, you can track how many numbers you've returned (or how many are still available) and recognize if you ran out of numbers.
The generated numbers could be tracked in a collection (Set). This means having an overhead of 32bit per number (when tracking available or generated numbers) plus the collection overhead. Another possibility is to use a boolean-array and mark which slots have been used. Again, this is an overhead, as booleans usually are stored as 32bit value.
But there's a cheaper way to store booleans: as packed bits in an integer. That's what java.util.BitSet does, so each boolean will occupy one bit.
Solution with BitSet and tracking how many numbers are available:
public class RandomNumbers {
private final Random random = new Random();
private final BitSet used = new BitSet();
private final int min = 10000;
private final int max = 99999;
private final int numbersAvailable = max - min + 1;
public static void main (String[] args) {
RandomNumbers randomNumbers = new RandomNumbers();
for (int i = 0; i < 100; i++) {
System.out.println(randomNumbers.nextRandom());
}
}
public int nextRandom () throws NoSuchElementException {
while (numbersAvailable > 0) {
int rnd = min + random.nextInt(max - min + 1);
if (!used.get(rnd)) {
used.set(rnd);
numbersAvailable--;
return rnd;
}
}
throw new NoSuchElementException();
}
}
Just
(int)(Math.random()*89999)+10000
After edit: (just not understood before edit) - you can put generated number in HashSet and after random just check if set contains new number (it will go very slow if you use it many times, but I think this is a good solution if you don't need a lot of numbers.
From my comment: After exceding about 50% of numbers I would create a collection of remaining numbers to pick, same as yours, but you should document in class, that it can freeze for a moment after 50% results usage and give ability to set this factor to client.
Maybe ther is a better way, depending of "how much randomness" must be in generated numbers (for example mathematical approach to sequence generator)
Seems pretty straightforward. A much simpler solution with less memory usage is to just create a set that will hold all the numbers you want like this:
Random random = new Random();
Set<Integer> randomNumbers = new HashSet<Integer>(10);
while(randomNumbers.size() < 10)
randomNumbers.add( new Integer(random.nextInt(89999) + 10000) );
And to view them all:
for(Integer randomNumber : randomNumbers){
System.out.println(randomNumber);
}
This will guarantee uniqueness to the properties of a set and greatly improve your memory usage.
Your method would indeed be ideal to create a large number of unique values, however if you are only creating a small number of unique values it can be more efficient to simply keep track of the used values to garantee uniqueness
import java.util.Collection;
import java.util.HashSet;
import java.util.Random;
public class UniqueRandom {
static Random rnd=new Random();
public static void main(String args[]){
Collection<Integer> alreadyChosen = new HashSet<Integer>();
for(int i=0;i<10;i++){
System.out.println(getNextUniqueRandom (alreadyChosen));
}
}
public static int getNextUniqueRandom(Collection<Integer> alreadyChosen){
if (alreadyChosen.size()==90000){ //hardcoded 5 figure numbers, consider making a variable
throw new RuntimeException("All 5 figure IDs used");
}
boolean unique=false;
int value=0;
while(unique==false){
value=rnd.nextInt(90000)+10000;
unique=!alreadyChosen.contains(value);
}
alreadyChosen.add(value);
return value;
}
}
This method is highly efficient when only a small proportion of the available range is required but becomes slower and slower as collisions become more common. The exact implementation you should choose is highly dependant upon how many values you need to get.
Notes to consider
As already stated this will get very very slow as more values are
chosen, should be made clear to end user, or even better; change algorithm after so many calls
Are there any existing utilities like Apache Commons StringUtils that make it easy to increment an integer, but output it as a zero padded string?
I can certainly write my own utilizing something like String.format("%05d", counter), but I'm wondering if there is a library that has this already available.
I'm envisioning something I can use like this:
// Create int counter with value of 0 padded to 4 digits
PaddedInt counter = new PaddedInt(0,4);
counter.incr();
// Print "0001"
System.out.println(counter);
// Print "0002"
System.out.println(counter.incr());
String text = "The counter is now "+counter.decr();
// Print "The counter is now 0001"
System.out.println(text);
I doubt you'll find anything to do this, because padding and incrementing are two basic operations that are unrelated, and trivial to implement. You could have implemented such a class three times in the time you took to write your question. It all boils down to wrapping an int into an object and implementing toString using String.format.
In case anyone is interested, I threw together this a few minutes after posting my question:
import org.apache.commons.lang.StringUtils;
public class Counter {
private int value;
private int padding;
public Counter() {
this(0, 4);
}
public Counter(int value) {
this(value, 4);
}
public Counter(int value, int padding) {
this.value = value;
this.padding = padding;
}
public Counter incr() {
this.value++;
return this;
}
public Counter decr() {
this.value--;
return this;
}
#Override
public String toString() {
return StringUtils.leftPad(Integer.toString(this.value),
this.padding, "0");
// OR without StringUtils:
// return String.format("%0"+this.padding+"d", this.value);
}
}
The only problem with this is that I must call toString() to get a string out of it, or append it to a string like ""+counter:
#Test
public void testCounter() {
Counter counter = new Counter();
assertThat("0000", is(counter.toString()));
counter.incr();
assertThat("0001",is(""+counter));
assertThat("0002",is(counter.incr().toString()));
assertThat("0001",is(""+counter.decr()));
assertThat("001",is(not(counter.toString())));
}
To be honest, I think you are mixing different concerns. An integer is an integer with all the operations and if you want to output it padded with zeros that is different thing.
You might want to have a look at StringUtils.leftPad as an alternative of String.format.