Jump to content
Frequently Asked Questions
  • Are you not able to open the client? Try following our getting started guide
  • Still not working? Try downloading and running JarFix
  • Help! My bot doesn't do anything! Enable fresh start in client settings and restart the client
  • How to purchase with PayPal/OSRS/Crypto gold? You can purchase vouchers from other users
  • Java Tips & Pitfalls


    Dioxin

    Recommended Posts

    Pitfalls

    These pitfalls revolve around excess object creation. Planned on including design pitfalls, but didn't want to over-complicate the thread. Hope you enjoy! 

    Hidden StringBuilders and Iterators

     

    The below example:

     

     

    ArrayList<String> words = ...;
    String sentence = "";
    for(String word : words) {
        sentence += word + " ";
    }
    Compiles into:

    ArrayList<String> words = ...;
    String sentence = "";
    Iterator<String> iterator = words.iterator();
    while(iterator.hasNext()) {
        String word = iterator.Next();
        sentence = new StringBuilder(sentence).append(word).append(" "). toString();
    }
    For those who may not know:
    • ArrayList#iterator() creates a new object every time it's called
    • String#toString() creates a new object every time it's called
    Instead, this could be written as:

    ArrayList<String> words = ...;
    StringBuilder builder = new StringBuilder();
    words.forEach(word -> builder.append(word).append(' '));
    String sentence = builder.toString();
    Since ArrayList#forEach uses a standard for-loop, iterator() is not called (saves an object). We also avoid 2 excess objects per loop iteration by using a StringBuilder from the start, rather than concatenation.

     

    LinkedList#forEach, as well as other collections, use iterator(). Due to their implementations, it's best to iterate rather than use indexed access.

     

    Autoboxing

     

    The code below:

    List<Integer> numberList = ...;
    for(int i = 0; i < 100; i++)
        numberList.add(i);
    Compiles into:

    List<Integer> numberList = ...;
    for(int i = 0; i < 100; i++)
        numberList.add(new Integer(i));
    This is due to generic's lack of primitive support. To work around this, the compile automatically wraps your primitive in an object. Autoboxing also occurs in the code below:

    Integer number = 5;
    In areas where autoboxing occurs often (such as many times a second), use primitive collection API. If unable to, use an array if possible.

     

    Immutable types with mutation behaviors

     

    There are quite a few immutable types in the standard library, the most popular being String. Anytime you attempt to mutate a String object, a new object is created.

     

    This means methods such as toLowercase(), toCharArray(), substring(..), etc.. all create new objects.

     

    The code below:

    String capitalizeFirstLetter(String name) {
        return name.substring(0, 1).toUppercase() + name.substring(1);
    }
    Would be more efficient if written as:

    String capitalizeFirstLetter(String name) {
        return Character.toUppercase(name.charAt(0)) + name.substring(1);
    }
    Although you can avoid the substring(1) by looping through and appending charAt to a StringBuilder, though that's hopping on the fence of readability vs performance.

     

    Tips 

    Be aware of the supported functional interfaces

     

    Some commonly used ones:

    Too many developers are unaware, creating their own interfaces to suit the needs that these interfaces provide.

     

    Use try-with-resources

     

    Although this released in Java 7, still too many developers have yet to add it to their arsenal.

     

    Beginner developers tend to either forget to close resources, or feel closing them may cause too much hastle as shown below:

    Socket socket = ...;
    try {
        //use socket
    } catch(IOException e) {
        e.printStackTrace();
    } finally {
        try {
            socket.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    Try-with-resources allows you to specify a resource (which must implement AutoCloseable) within the try statement:

    try(Socket socket = ...) {
        //use socket
    } catch(IOException e) {
        e.printStackTrace();
    }
    The resource will be closed as soon as the try block ends, even if an exception is thrown.

     

    Use BufferedReader#lines()

     

    This is an oddly specific tip, but an extremely useful Java 8 addition to the BufferedReader API, which I've seen used quite often in independent projects.

     

    Rather than using a loop to read data in line by line via readLine():

    try(BufferedReader reader = ...) {
        String line;
        while((line = reader.readLine()) != null) {
            //use line
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
    BufferedReader now exposes a Stream for lines, allowing you to consume the lines in a neater, for functional fashion:

    try(BufferedReader reader = ...) {
        reader.lines().forEach(line -> {
            //use line
        });
    } catch(IOException e) {
        e.printStackTrace();
    }
    For those who understand streams, no further explanation is really needed, as you can perform many powerful operations using them, such as filtering, flattening, reducing and much more.

     

    Deeper Understanding

    Many of these concepts get left in the dust due to their easy solutions. Although these could be considered pitfalls, I felt they deserved their own section due how common-place, yet unstudied these concepts are.

    ConcurrentModificationException

     

    Reproduce:

    List<String> list = new ArrayList<>();
    //fill list
    for(String string : list) {
        list.remove(string);
    }
    Occurs when:
    • Using an Iterator to iterate over a collection, then attempting to remove an element directly from the collection.
    Solution:Comments:

     

    Since iteration isn't always index based, removing an element may corrupt the iteration chain. This is why you must remove elements using the iterator, which will remove the current element when Iterator#remove() is called. The exception is thrown to avoid potential corruption; iterators fail-fast, which is a popular safety precaution.

     

    Comparing Strings using ==

     

    Reproduce:

     

     

     

     

     

     

    String first = new String("hi");
    String second = new String("hi");
    String third = "hi";
    String fourth = "hi";
    
    System.out.println(first == second); //false
    System.out.println(first.equals(second)); //true
    System.out.println(third == fourth); //true
    System.out.println(third.equals(fourth)); //true
    Occurs when:
    • Comparing two different String objects with the same character values.
    Solution:
    • Always compare Strings using .equals when comparing their values (letters).
    Comments:

     

    == compares references (if two variables reference the same object). This can cause some confusion with Strings, since sometimes it may work, while other times it doesn't.

     

    Since String objects are common, Java has a system to optimize String memory usage: the String Pool. When creating a String using a literal (quotation marks), the object is added to a pool. When another literal containing the same letters is created, the object from the pool is reused.

     

    This is why third == fourth prints true. This only applies when a variable reference a literal, which is why first == second prints false. Very rarely will you need to compare String references, so always prefer .equals if you want to compare Strings, unless you know what you're doing (actually want to compare references).

     

    Link to comment
    Share on other sites

    Archived

    This topic is now archived and is closed to further replies.

    ×
    ×
    • Create New...

    Important Information

    We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.