design, development, java, technology

ten commandments for Java programmers

There are many standards and best practices for Java Developers out there. The article 10 Commandments for Java Developers outlines ten most basic rules that every developer must adhere to and the disastrous outcomes that can follow if these rules are not followed. Briefly summarizing the 10 commandments:

    1. Add comments to your code. – It is true comments do not literally contribute to the functionality of a program. But time and time again you return to the code that you wrote two weeks ago and, for the life of you, you cannot remember what it does!
    2. Do not complicate things. – Developers tend to come up with complicated solutions for the simplest problems. We introduce EJBs into applications that have five users. We implement frameworks that an application just does not need. We add property files, object-oriented solutions, and threads to the application that do not require such things. For those who do not know any better, I recommend reaching out to the more experienced programmers for advice.
    3. Keep in Mind – “Less is more” is not always better. – Code efficiency is a great thing, but in many situations writing fewer lines of code does not improve the efficiency of that code.
    4. No hard coding, please. – Developers often forget or omit this rule on purpose because we are, as usual, crunched for time. But maybe if we had followed this rule, we would not have ended up in the situation that we are in. How long does it take to write one extra line of code that defines a static final variable?
    5. Do not invent your own frameworks. – There are literally thousands of frameworks out there and most of them are open-source. Many of these frameworks are superb solutions that have been used in thousands of applications. We need to keep up to date with the new frameworks, at least superficially. One of the best and most obvious examples of a superb widely used framework is Struts. This open source web framework is a perfect candidate to be used in web-based applications.
    6. Say no to Print lines and String Concatenations. – I know that for debugging purposes, developers like to add System.out.println everywhere we see fit. And we say to ourselves that we will delete these later. But we often forget to delete these lines of code or we do not want to delete them.
    7. Pay attention to the GUI. – No matter how absurd it sounds; I repeatedly observe that GUI is as important to the business clients as functionality and performance. The GUI is an essential part of a successful application. Very often IT management tends to overlook the importance of GUI. Many organizations save money by not hiring web designers who have experience in design of “user-friendly” applications. Java developers have to rely on their own HTML skills and their limited knowledge in this area.
    8. Always Prepare Document Requirements. – Every business requirement must be documented. This could be true in some fairy tale, but it is far from that in the real world. No matter how time-pressed your development is, no matter how tight the deadlines, you must always make sure that every business requirement is documented.
    9. Unit-test. Unit-test. Unit-test. – I am not going to go into any details as to what is the best way to unit-test your code. I am just going to say that that it must be done. This is the most basic rule of programming. This is one rule that, above all, cannot be omitted. It would be great if your fellow developer could create and execute a test plan for your code, but if that is not possible, you must do it yourself. When creating a unit test plan, follow these basic rules:
      1. Write the unit test before writing code for the class it tests.
      2. Capture code comments in unit tests.
      3. Test all the public methods that perform an “interesting” function (that is, not getters and setters, unless they do their getting and setting in some unique way).
    10. Remember – quality, not quantity. – Do not stay late (when you do not have to). I understand that sometimes production problems, urgent deadlines, and unexpected events might prevent us from leaving work on time. But, managers do not appreciate and reward their employees because they stay late on regular basis, they appreciate them because they do quality work. If you follow the rules that I outline above, you will find yourself producing less buggy and more maintainable code. That is the most important part of your job.

 

Standard
apis, java, technology

TIOBE Programming Community Index

The TIOBE Programming Community index gives an indication of the popularity of programming languages. The index is updated once a month. The ratings are based on the number of skilled engineers world-wide, courses and third party vendors. The popular search engines Google, MSN, Yahoo!, and YouTube are used to calculate the ratings. Observe that the TIOBE index is not about the best programming language or the language in which most lines of code have been written.

The index can be used to check whether your programming skills are still up to date or to make a strategic decision about what programming language should be adopted when starting to build a new software system.

Since there are many questions about the way the TIOBE index is assembled, a special page is devoted to its definition.

The ratings are calculated by counting hits of the most popular search engines. The search query is executed for the regular Google, Google Blogs, MSN, Yahoo!, and YouTube web search for the last 12 months. The web site Alexa.com has been used to determine the most popular search engines.

The number of hits determine the ratings of a language. The counted hits are normalized for each search engine for the first 50 languages.

Besides the rating of programming languages, there is also a status indicated in the TIOBE chart. Programming languages that have status “A” are considered to be mainstream languages. Status “A-” and “A–” indicate that a programming language is between status “A” and “B”. If a programming language has a rating that is higher than 0.7% (yes, this number is arguable but we had to fix it somewhere) for at least 3 months it is rewarded status “A”. The first two months the programming language will receive status “A–” and “A-” respectively. The opposite holds for languages that go from status “A” to status “B”. So if a language had status “A” 2 months ago, a rating of “0.607%” last month and a rating of “0.687%” now, it will have status “A–“.

Programming languages that are very similar are grouped together. Currently the maximum of the hits of the individual languages is taken into account when calculating the ratings of groupings. In the future we will do a better job and take the union (from mathematical set theory) of all the hits.

The long term trends for the top 10 programming languages can be found in the line diagram below.

No wonder Java tops the ranking. And it will continue to hold that position for some time to come. What I am surprised to see there was Pascal is gaining popularity and people are using it nowadays. You can see the list of top 50 languages here.

Standard
java, technology

Which is the Hottest Java Web Framework?

The “Break it Down” Blog has a lengthy post on Which is the Hottest Java Web Framework? Or Maybe Not Java? Comparing Java Web Frameworks is hard because so many people are passionate about the framework they know best. Add a couple more like Flex and Ruby on Rails and its downright difficult. Nevertheless, this post is good in that it contains a lot of pretty trend graphs and it looks like the author has done some good research. It’s likely the folks that will scream foul are the ones that did poor in the comparison (Tapestry and Stripes, I’m talking about you).

Surprising among the top Java Web Frameworks is the rise of Struts 2. Which is much more interesting I think is how Wicket adoption has stayed almost flat while Struts2 adoption has spiked. Spring MVC/WebFlow seems to be going no where fast and racing JBoss Seam there.The popularity of Struts 2 really caught me off guard with it being quite a bit different from Struts 1, I figured it got thrown into the “just another web framework” category, but I guess there is something in a name and it’s doing quite well.

Regardless of what one might think of the post and trends, one has appreciate the amount of time the author invests in the work.

Standard
java

Java Launcher

Java Launcher is a powerful Java tool which can run java applications and applets by double-clicking class files in explorer as if they are normal windows executable files.

Java Launcher includes nine features totally. Six features in Windows explorer: Run Java applications and applets by double-clicking class files; view class source codes and class hierarchies in graphic format by right-clicking; display contents of jar and zip files without extracting them by right-clicking and allow to save selected files from jar or zip to disk; compile thousands of Java files by right-clicking, execute and debug thousands of applications and applets by right-clicking. Two features of creating: create Windows EXE files from Java applications with user icons, arguments of Java main method, system and user classpaths; create executable JAR files. Last feature is named Java-Help-System, which can auto generate an advanced Help System for J2SE documentations without spending user’s any time and energy.

Click here to know more

Standard
java

Java 6u10 with many features

Java Kernel

Java Kernel is a new distribution aimed at getting Java software up and running faster. Instead of a full JRE, users download a small installer (the “kernel”) which includes the most commonly needed JRE components. Additional components are downloaded as needed, and the JRE will download remaining components in the background and then reassemble itself.

More information about Java Kernel can be found in the 6u10 FAQ.

Java Plug-in

Java SE 6u10 includes a brand-new implementation of the Java Plug-in, which is used by default as long as you are using Firefox 3 or Internet Explorer. The next-generation plug-in runs applets outside of the browser in one or more separate processes. Applets still appear inside of the web browser window as they always have, but this means that it is now possible to use different JRE versions, command-line arguments, and configurations to run different applets. The isolation provided by running the web browser and the JRE — two very large, very complex pieces of software — in separate process spaces improves the reliability of both, and gives applets the same flexibility and control over JRE configurations that other Java software has always enjoyed.

New Plug-In Advantages:

  • Improved reliability
  • Improved JavaScript communication
  • Per-applet control of JRE command-line arguments
  • Per-applet control of JRE memory settings, larger maximum heaps
  • JNLP support
  • Per-applet JRE version selection
  • Improved Vista support

Much more information about the new plug-in can be found in the release notes.

Java Deployment Toolkit

The Java Deployment Toolkit makes deploying Java applets or Java Web Start programs a snap. The Deployment Toolkit JavaScript file provides:

  • Accurate detection of installed JREs
  • Seamless JRE installation
  • Complete applet launching (JRE detection and, if necessary, upgrading) in a single line of code
  • Complete Web Start program launching in a single line of code

The following HTML code is all it takes to ensure that Java 1.6 is installed and then a Java applet is launched:

<script src="http://java.com/js/deployJava.js"></script>
    
<script>
  deployJava.runApplet({codebase:"http://www.example.com/applets/",
     archive:"ExampleApplet.jar", code:"Main.class",
     width:"320", Height:"400"}, null, "1.6");
</script>

More documentation about the deployment toolkit can be found here.

Nimbus Look and Feel

Metal look and feel for Swing was the only competition for the Windows 95 interface in Java. Given the state of graphical user interfaces a decade ago, Metal was an attractive and elegant alternative to the other common interfaces of the time.

The updated Ocean theme in Java SE 5 helped to keep Metal a viable choice up to the present day, but it’s time for Swing’s cross-platform look and feel to get an overhaul.

Enter the Nimbus Look and Feel. A brand new, modern look and feel based on Synth, Nimbus provides a polished look to applications which choose to use it. And because Nimbus is drawn entirely using Java 2D vector graphics, rather than static bitmaps, it’s tiny (only 56KB!) and can be rendered at arbitrary resolutions.

Swing in Metal

Swing in Nimbus

For compatibility reasons, Metal is still the default Swing look and feel, but updating applications to use Nimbus couldn’t be simpler. It only takes a single line of code:

UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");

You can also force Nimbus to be the default look and feel by specifying -Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel on the command line. A more permanent way to set the property is to add

swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

to the file <JAVA_HOME>/lib/swing.properties. You will have to create the swing.properties file if it does not already exist.

For further reading about Nimbus, take a look at the Nimbus early access page.

Standard
java

Just-in-time compilation

In computing, just-in-time compilation (JIT), also known as dynamic translation, is a technique for improving the runtime performance of a computer program. JIT builds upon two earlier ideas in run-time environments: bytecode compilation and dynamic compilation. It converts code at runtime prior to executing it natively, for example bytecode into native machine code. The performance improvement over interpreters originates from caching the results of translating blocks of code, and not simply reevaluating each line or operand each time it is met (see Interpreted language). It also has advantages over statically compiling the code at development time, as it can recompile the code if this is found to be advantageous, and may be able to enforce security guarantees. Thus JIT can combine some of the advantages of interpretation and static compilation.

Several modern runtime environments, such as Microsoft‘s .NET Framework and most implementations of Java and most recently Actionscript 3, rely on JIT compilation for high-speed code execution.

In a bytecode-compiled system, source code is translated to an intermediate representation known as bytecode. Bytecode is not the machine code for any particular computer, and may be portable among computer architectures. The bytecode may then be interpreted, or run, on a virtual machine. A just-in-time compiler can be used as a way to speed up execution of bytecode. At the time the bytecode is run, the just-in-time compiler will compile some or all of it to native machine code for better performance. This can be done per-file, per-function or even on any arbitrary code fragment; the code can be compiled when it is about to be executed (hence the name “just-in-time”).

In contrast, a traditional interpreted virtual machine will simply interpret the bytecode, generally with much lower performance. Some interpreters even interpret source code, without the step of first compiling to bytecode, with even worse performance. Statically compiled code or native code is compiled prior to deployment. A dynamic compilation environment is one in which the compiler can be used during execution.

A common goal of using JIT techniques is to reach or surpass the performance of static compilation, while maintaining the advantages of bytecode interpretation: Much of the “heavy lifting” of parsing the original source code and performing basic optimization is often handled at compile time, prior to deployment: compilation from bytecode to machine code is much faster than compiling from source. The deployed bytecode is portable, unlike native code. Since the runtime has control over the compilation, like interpreted bytecode, it can run in a secure sandbox. Compilers from bytecode to machine code are easier to write, because the portable bytecode compiler has already done much of the work.

References

John Aycock, “A brief history of just-in-time”
Wiki, Just-in-time compilation

Standard
articles, java

Is Java Windows for Unix?

Is Java Windows for Unix? by ZDNet‘s Paul Murphy — The key conceptual difference between C and Java as used in business applications development and run-times is simply this: the C you learn in school is the C you find in things like Solaris, but the Java you learn in school has very little, if anything, to do with the Java you see in things like Sun’s identity management packages.

Standard
java

Cloning Java Objects

Objects in Java are referred using reference types, and there is no direct way to copy the contents of an object into a new object. The assignment of one reference to another merely creates another reference to the same object. Therefore, a special clone() method exists for all reference types in order to provide a standard mechanism for an object to make a copy of itself. Here are the details you need to know about cloning Java objects.

Why create a local copy?

The most probable reason for creating a local copy of an object is because you plan to modify the object, and you don’t want to modify the method caller’s object. If you decide that you need a local copy, you can perform the operation by using the clone() method of the Object class. The clone() method is defined as protected, but you must redefine it as public in all subclasses that you might want to clone.

For example, the standard library class ArrayList overrides clone(), so you can call clone() for ArrayList, like this:



import java.util.*;class MyInt {

    private int i;

    public MyInt(int ii) { i = ii; }

    public void increment() { i++; }

    public String toString() {

        return Integer.toString(i);

    }

}

public class Test {

    public static void main(String[] args) {

        ArrayList al = new ArrayList();

        for(int i = 0; i < 10; i++ )

            al.add(new MyInt(i));

        ArrayList al1 = (ArrayList)al.clone();

        // Increment all al1's elements:

        for(Iterator e = al1.iterator(); e.hasNext(); )

            ((MyInt)e.next()).increment();

    }

}

The clone() method produces an Object, which must be recast to the proper type. This example shows how ArrayList’s clone() method does not automatically try to clone each of the objects that the ArrayList contains — the old ArrayList and the cloned ArrayList are aliased to the same objects. This is often called a shallow copy, since it’s only copying the “surface” portion of an object. The actual object consists of this “surface,” plus all the objects that the references are pointing to and all the objects those objects are pointing to, etc. This is often referred to as the “Web of objects.” When you copy the entire mess, it is called a deep copy.

The Cloneable interface and deep copies

By default, classes in Java do not support cloning; the default implementation of the clone() method throws a CloneNotSupportedException. You should override implementation of the clone() method. Remember that you must make it public and, inside the method, your first action must be super.clone(). Classes that want to allow cloning must implement the marker interface Cloneable. Since the default implementation of Object.clone only performs a shallow copy, classes must also override clone to provide a custom implementation when a deep copy is desired. Basically, if you want to make objects of your class publicly cloneable, you need code like this:


class Test implements Cloneable{
    ...
    public Object clone(){
        try{
            return super.clone();
        }catch ( CloneNotSupportedException e ){
            return null;
        }
    }
...
}

If you are happy with a protected clone, which just blindly copied the raw bits of the object, you don’t need to redefine your own version. However, you will usually want a public one. (Note: You can’t create a private or default scope clone; you can only increase the visibility when you override.)

Possible problems and a solution

Since the clone() method is protected, subclasses have to explicitly agree to be cloneable by overriding this protected method with a public method. All of the Collections classes do this. The subclass also has to implement Cloneable for the default cloning mechanism in Object.clone() to work.

If you have an object that you know has a public clone() method, but you don’t know the type of the object at compile time, you have problems. For instance, say x is declared as an Object. You can’t just call x.clone() because Object.clone() is protected. If Cloneable defined a public clone() method, you could use ((Cloneable) x).clone(), but it doesn’t. You either have to enumerate all the classes that you think x could be, or you have to resort to reflection.

Another problem arises when you try deep copying of a complex object. You’re assuming that the clone() method of all member object variables also does deep copy; this is too risky of an assumption. You must control the code in all classes, or you must know that all classes involved in deep copy operation do such a copy in the right way.

One solution to these problems is to clone using serialization. Serialization is usually used to send objects off somewhere (such as into a file or over the network) so that somebody else can reconstruct them later. You can abuse serialization to immediately reconstruct the object yourself. If the object is serializable at all, the reconstruction should be a faithful copy. In normal uses of serialization, the original object is nowhere near a faithful copy; it could be on the other side of the world at the far end of a network connection. You can be sure that changing the copy will have no effect on the original.

Standard
java

Developing real-time applications with Java RTS 2.0

Developing real-time applications with Java RTS 2.0 by Peter Mikhalenko at TechRepublic .

Java Real-Time System (RTS) 2.0 is Sun’s fully compliant implementation of the industry standard set of extensions for the Java platform. It helps you set process priorities according to importance (this is typically not supported in Java software applications).

If you run a normal Java application under Java RTS 2.0, nothing really changes. You’ll start to notice the differences when you need to develop a real-time application. There are, however, significant improvements in the latest version of Java RTS.

Features and enhancements in Java RTS 2.0

A key feature of Java RTS 2.0 is a very predictable method for recycling memory within Java software programs. Using this method, you can determine how and when functions should be executed — down to a millisecond or less.

The Java RTS 2.0 provides the following enhancements to the previous version:

  • Improved determinism for long-running applications.
  • Application monitoring, management, and troubleshooting functionality.
  • A new DTrace probe provider for Java RTS.
  • New MXBean to toggle between deterministic and debugging modes.
  • Modified acquisition of inherited access control for no-heap real-time threads (NHRTs).
  • Minor bug fixes.

Two enhancements in this new version that I think deserve calling special attention to are: its real-time garbage collector and the ability to create and assign a processor set.

Tune the real-time garbage collector
The main feature of Java RTS 2.0 is its real-time garbage collector (RTGC), which makes it fundamentally easier to approach real-time solutions and to control and tune applications. You can convert your existing Java threads to real-time threads (RTTs) by renaming them and yet still having the same semantics. Now you can start using the RTGC; you can set the priority of the thread even higher than the priority of the garbage collector and have no more suffering from the garbage collector. This used to be a very annoying problem for time-critical applications.

Java RTS 2.0 supports two garbage collectors: the RTGC and the non-real-time serial garbage collector. The RTGC, which is the default, might exhibit lower throughput than non-real-time serial garbage collectors, particularly on uniprocessors. For applications that are more sensitive to the collector’s throughput than to the pause times induced by the collector’s execution, the RTGC can be turned off with the -XX:-UseRTGC option. In this case, the non-real-time serial garbage collector is used, and all threads except NHRTs might suffer from pause times caused by garbage collection.

The RTGC is truly concurrent, thus it can be preempted at any time. There is no need to run the RTGC at the highest priority, and there is no stop-the-world phase, where all the application’s threads are suspended during the garbage collection execution. On a multiprocessor, one CPU can be doing some garbage collection work, while an application thread is making progress on another CPU.

The only time RTGC prevents a thread from running is when it has to look at a particular thread’s Java stack, where the local variables and operands are stored; hence, the main potential source of pause time for a given thread is caused by the scanning of its stack. Since a thread is not impacted by the scanning of the other thread stacks, the pause time for a thread is smaller than with non-concurrent garbage collectors.

For additional information, check out the Garbage Collection Guide for Java RTS 2.0.

Create and assign processor sets
For better temporal behavior on a multiprocessor machine, you can create a processor set devoted to the exclusive use of Java RTS 2.0. You can also dedicate a separate processor set to the exclusive use of the NHRTs and the RTTs of Java RTS 2.0. This partition of the available processors ensures the best temporal behavior for these threads by reducing cache thrashing effects.

To request Java RTS 2.0 to assign the NHRT threads to an existing dedicated processor set, use the -XX:RTSJBindNHRTToProcessorSet=<processor_set_id> option. The processors assigned to this processor set should all be set to no-intr to minimize latency and jitter. If this option is set, calling Runtime.availableProcessors from an NHRT will return the number of processors that are available in the processor set that has been assigned to NHRTs. You can use a similar option, RTSJBindRTTToProcessorSet, to bind RTTs to an existing dedicated processor set.

The Solaris operating system allows a machine’s processors to be partitioned into a number of smaller, nonoverlapping processor sets. Processors assigned to a processor set are reserved for the processes explicitly bound to that set. Processes bound to that processor set cannot use those processors. You can create a new processor set with the following command (run with the superuser privileges):

# /usr/sbin/psrset -c
   created processor set <pset id>

Then, you can assign processors to this new processor set with the following command:

# /usr/sbin/psrset -a <pset id> <cpu id>

where <cpu id> refers to one of the on-line processors displayed by the following command:

# /usr/sbin/psrinfo
 <cpu id>     on-line   since mm/dd/yyyy hh:mm:ss
 <cpu id>     on-line   since mm/dd/yyyy hh:mm:ss
 <cpu id>     on-line   since mm/dd/yyyy hh:mm:ss
 <cpu id>     on-line   since mm/dd/yyyy hh:mm:ss

Now you can assign the Java RTS Virtual Machine to run in the new processor set, as follows:

# /usr/sbin/psrset -e <pset id> <Java RTS command line>

How Java RTS differs from the Java HotSpot VM

Java RTS differs in functionality from Java SE and the Java HotSpot VM in a number of ways; it has clocks, timers, class initialization, memory management, and scheduling. In addition, certain issues are only applicable to Java RTS; this includes programming considerations, limitations, bugs, and workarounds.

There are also general differences in the way that Java RTS behaves in comparison to the Java HotSpot VM, which include the following:

  • The default values for the command-line parameters have been tuned for Java RTS and might differ from the default values in the Java HotSpot VM. For instance, the default values for -Xms and -Xmx have been increased for Java RTS.
  • All the memory that is specified by the -Xms and -Xmx parameters is acquired and locked at initialization of the Java HotSpot VM.
  • Java RTS has limited support for the monitoring, management, and troubleshooting tools.
  • Since NHRTs exist, classes and interned strings are never garbage collected. The Real-Time Specification for Java (RTSJ) also prevents objects allocated in static initializers from being garbage collected. You should be aware that such objects are allocated in immortal memory and survive until the Java HotSpot VM exits.
  • Java RTS only provides is the client compiler. The server compiler depends on nondeterministic optimization; therefore, it has been deactivated.
  • Just-In-Time (JIT) compilation is asynchronous by default in Java RTS.
  • Java RTS provides the implementation of variants of inline caches and virtual method inlining. These variants are especially intended for use with the RTGC.
  • Java RTS only provides the 32-bit “client” version of the Java HotSpot VM; the 64-bit version is not supported and using the -server option has no effect.
  • Java RTS 2.0 is currently supported on an UltraSPARC processor or on a Sun x86/x64 platform running Solaris 10 (update 3 or 4 only); Java RTS 2.1 for Linux is available as early access now (real-time Linux versions only): SUSE Linux Enterprise Real Time 10 and Red Hat Enterprise MRG 1.0.
  • It is highly recommended to have a minimum of two processors.

Additional resources about Java RTS

Blogged with Flock

Standard
java

Random Number Generator

You might have wondered how predictable machines like computers can generate randomness. In reality, most random numbers used in computer programs are pseudo-random, which means they are generated in a predictable fashion using a mathematical formula. This is fine for many purposes, but it may not be random in the way you expect if you’re used to dice rolls, roulette wheels and lottery draws.

A list of random number is a numerical sequence as defined in Wiki – “A numeric sequence is said to be statistically random when it contains no recognizable patterns or regularities; sequences such as the results of an ideal die roll, or the digits of π (as far as we can tell) exhibit statistical randomness…

We can write a random number generator to generate a list of random numbers in a given range. A random number generator (often abbreviated as RNG) is a computational or physical device designed to generate a sequence of numbers or symbols that lack any pattern, i.e. appear random. Computer-based systems for random number generation are widely used, but often fall short of this goal, though they may meet some statistical tests for randomness intended to ensure that they do not have any easily discernible patterns. Methods for generating random results have existed since ancient times, including dice, coin flipping, the shuffling of playing cards, the use of yarrow stalks in the I Ching, and many other techniques.

Intuitively, an algorithmically random sequence (or random sequence) is an infinite sequence of binary digits that appears random to any algorithm. The definition applies equally well to sequences on any finite set of characters. Random sequences are key objects of study in algorithmic information theory.

I had written a small program in Java to generate a sequence of unique random numbers within 20.

The program is here: Random Number Generator

I am creating a List in which I intend to store unique number between 1 and 20 (inclusive). Firstly, I have to get hold of the object of class Random In order to store only unique numbers, first I check after generating the random number by

rd.nextInt(20);

whether that number already exists in the list. And, thats it. You have got a list of unique random numbers between 1 and 20.

Blogged with Flock

Tags: , , , ,

Standard