The potential problem with tracking the 'used' items is that there may be many more iterations than needed to output all the items. For example, lets say element 10 was the first item displayed, by tracking it as used, its possible that 10 will come up in randrange() again before all items have been displayed. The more items that are 'used' the higher the likelihood that we will have to add an iteration to get an element that was not used.
One way we can make sure we only make 6000 iterations is to reduce the size of the array with each iteration and do randRange( 1, arrayLen({array name}).
//set up array
test = [];
for( x=1; x<=6000; x++ ){
arrayAppend( test, "Item " & x );
}
//here is the meat of the process
done = false;
count = 1;
while( !done ){
index = randRange( 1, arrayLen( test ) );
writeOutput( count & " : " & test[ index ] & "<br/>" );
arrayDeleteAt( test, index );
count++;
done = !arrayLen( test );
}
The code above was averaging under 100ms to execute on my development machine.
Also, after testing out Henry's code, I noticed that it could be tweaked a little, here is the slightly streamlined code.
<!--- set up the array --->
<cfset theString = []>
<cfloop from="1" to="6000" index="i">
<cfset arrayAppend(theString, "Item : " & i) />
</cfloop>
<cfset collections = createObject("java","java.util.Collections") />
<cfset collections.shuffle( theString ) />
<cfoutput>
<cfloop array="#theString#" index="x">
#x#<br/>
</cfloop>
</cfoutput>
Notice I got rid of the arrayresize() and that you can simply pass the original array into collections.shuffle() and then loop over that.