Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 3486820
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 18, 20262026-05-18T11:04:34+00:00 2026-05-18T11:04:34+00:00

For one of my applications, the following function has to be called very often.

  • 0

For one of my applications, the following function has to be called very often. This function takes up a lot of CPU and hence I am wondering if you know how to improve the performance.

The code counts the occurrences of a combination of four characters. During testing, I found that the number of entries in the map are around 100. The length of text is in the range of 100 to 800. The initial size of 200 is a guess and the code seems to run faster than without specifying an initial size. It is probably not the optimal value, though.

private Map<String, Integer> getTetagramCount(final String text) {
    final Map<String, Integer> cipherTetagrams = new HashMap<String, Integer>(200);

    for (int i = 0; i < text.length() - 4; i++) {
        final String tet = text.substring(i, i + 4);

        final Integer count = cipherTetagrams.get(tet);
        if (count != null) {
            cipherTetagrams.put(tet, count + 1);
        } else {
            cipherTetagrams.put(tet, 1);
        }
    }

    return cipherTetagrams;
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-18T11:04:35+00:00Added an answer on May 18, 2026 at 11:04 am

    I do a lot of work in NLP and machine learning, so I have to do this sort of thing all the time, and there are a ton of opportunities for optimization.

    A few points to consider:

    1. First of all, you’re getting killed by the standard JDK HashMap class. It’s a nice container for general-purpose computing, but it’s terrible for high-performance computing. For each entry into your collection (a four-character string (8 bytes) and an integer (4 bytes), the standard java HashMap will consume:

      • A string object
        • 8-byte object overhead
        • 4-byte reference to an array
        • 4-byte string-length field
      • A character array
        • 8-byte object overhead
        • 2-bytes for each character (times 4 characters) = 8 bytes
        • 4-byte array-length field
      • An Integer object
        • 8-byte object overhead
        • 4-byte int value
      • A HashMap.Entry object
        • 8-byte object overhead
        • 4-byte Key reference
        • 4-byte Value reference

      So your tiny little 12 bytes of data becomes 64 bytes. And that’s before the HashMap has allocated an array of hash values to use during its lookup operations. Keep in mind that all those tiny little objects mean more work for the GC, but more importantly, it means that your objects span a larger swath of main memory and are less likely to fit within the CPU cache. When you have lots of cache misses, you lose performance.

      NOTE: A commenter reminded me that all the substrings will share the same underlying character array, which is a good point that I’d forgotten about. But still, that means each map entry goes from being 64 bytes down to 44 bytes. Which is still a shame, when there should only be 12 bytes.

    2. Boxing and unboxing all those integer values causes your code to run more slowly and to consume more memory. In most cases, we don’t really care about that, and the vanilla HashMap implementation is fine, even with its mandatory boxing and greedy memory consumption. But in your case, if this code is run within a tight inner loop, we’d rather have a specialized class that knows its values will always be integers and eliminates the need for boxing.

    3. If you dig down into the JDK source code, you’ll see that your code will end up invoking string’s hashCode() and equals() methods twice. Once for the map.get() and once for the map.put(). But there’s a different kind of collection called a HashBag that can perform lookup, insertion, and count incrementation with only one lookup. A "bag" collection is kind of like a "set", except that it can contain duplicates, and it keeps track of how many duplicates there are. For each of your tetragrams, you’d just call bag.put(tetragram) without having to first retrieve and update a count. Unfortunately, there are no bag implementations in the JDK, so you’ll need to find one elsewhere, or write one yourself.

    4. Luckily, your tetragrams can be losslessly encoded as long values (since each java character is 2 bytes wide, and a long gives you eight bytes to work with). You can therefore iterate through the character array and convert each tetragram to a long, and avoid all the overhead of constructing so many tiny strings. Then, you can keep your results in a LongIntHashMap (from the Trove library). This will be much faster than your current implementation, because you can avoid creating all those tiny little string objects.

    5. Although the Trove LongIntHashMap is quite excellent, it isn’t quite as good as a LongHashBag would be. There’s no equals invocation (since longs can be compared with the == operator), but you’ll still pay the price of two hashCode invocations. If you want to get really aggressive with optimization, you could look at the source code of the LongIntHashMap and figure out how to modify it into a LongHashBag. It’s not that difficult, and ultimately, that’s exactly what I’ve done in my own code.


    Update 1:

    Okay, here’s a little bit of code:

    private LongHashBag countTetragrams(String text) {
    
      // Homework assignment: find a good LongHashBag implementation, or
      // grab the LongIntHashMap implementation from Trove, and tweak it
      // to work as a Bag
      LongHashBag bag = new LongHashBag(500);
    
      // There are no tetragrams in this string.
      if (text.length() < 4) return bag;
    
      // Shortcut: if we calculate the first tetragram before entering
      // the loop, then we can use bit-shifting logic within the loop
      // to create all subsequent tetragram values.
      char[] c = text.toCharArray();
      long tetragram = ((long) c[0] << 48) |
         (((long) c[1]) << 32) |
         (((long) c[2]) << 16) |
         ((long) c[3]);
    
      bag.add(tetragram);
    
      for (int i = 4, last = text.length(); i < last; i++) {
         // During each loop iteration, the leftmost 2-bytes are shifted
         // out of the tetragram, to make room for the 2-bytes from the
         // current character.
         tetragram = (tetragram << 16) | ((long) c[i]);
         bag.add(tetragram);
      }
    
      return bag;
    }
    

    Update 2:

    I just did some testing of various solutions, and I was about to get about a 25% performance improvement using the LongHashBag approach instead of the standard HashMap approach.

    However, I was about to get a 300% improvement by recycling the resultant objects. Basically, instead of doing this:

    private LongHashBag countTetragrams(String text) {
    
      // Creates a new HashBag on every invocation. Very wasteful.
      LongHashBag bag = new LongHashBag(500);
    
      // ...blah blah blah...
    
      return bag;
    }
    

    …I’m now doing this…

    private void countTetragrams(String text, LongHashBag bag) {
    
      // Return the object to a neutral state, and recycle it.
      bag.clear()
    
      // ...blah blah blah...
    }
    

    The calling code is responsible for creating the LongHashBag object and ensuring that we’re done with it by the time we call the count method again.

    But it would also work to do this…

    private LongHashBag countTetragrams(String text) {
    
      // Return the object to a neutral state, and recycle it.
      LongHashBag bag = retrieveLongHashBagFromObjectPool();
    
      // ...blah blah blah...
      return bag;
    }
    

    … which will add a little bit of overhead for maintaining the pool. And the calling code would have to remember to put the bag back into the pool when it finishes using it. But the performance benefits could definitely be worth it.

    Incidentally, these are exactly the kinds of tricks I use every day. Object pooling has become one of my most reliable tricks for improving performance.

    But like I said, recycling those objects yields a 300% performance improvement.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

One of my applications has property of images as background of Cells in UITableView,
I am reading from MSDN this: A child window has only one parent window,
Lets say I have the following one-to-many relationship: Site has many Users User belongs
I have the following code in one of my Rails 3 application views: <ul>
I have the following patterns and grouping application which I am refactoring in one.
I have created one dynamic button in my application and I call the following
Can any one tell me how to solve the following scenario in Asp.net application.
Using linkedin-j , I have the following code in one part of my application
One of our applications is exhibiting poor network behavior on Vista due to the
One of our applications is started suddenly terminate with error Can't load file or

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.