title: Fastest way to strip all non-printable characters from a Java String
body: <p>What is the fastest way to strip all non-printable characters from a <code>String</code> in Java?</p>
  
  <p>So far I've tried and measured on 138-byte, 131-character String:</p>
  
  <ul>
  <li>String's <code>replaceAll()</code> - <strong>slowest method</strong>
  
  <ul>
  <li>517009 results / sec</li>
  </ul></li>
  <li>Precompile a Pattern, then use Matcher's <code>replaceAll()</code>
  
  <ul>
  <li>637836 results / sec</li>
  </ul></li>
  <li>Use StringBuffer, get codepoints using <code>codepointAt()</code> one-by-one and append to StringBuffer
  
  <ul>
  <li>711946 results / sec</li>
  </ul></li>
  <li>Use StringBuffer, get chars using <code>charAt()</code> one-by-one and append to StringBuffer
  
  <ul>
  <li>1052964 results / sec</li>
  </ul></li>
  <li>Preallocate a <code>char[]</code> buffer, get chars using <code>charAt()</code> one-by-one and fill this buffer, then convert back to String
  
  <ul>
  <li>2022653 results / sec</li>
  </ul></li>
  <li>Preallocate 2 <code>char[]</code> buffers - old and new, get all chars for existing String at once using <code>getChars()</code>, iterate over old buffer one-by-one and fill new buffer, then convert new buffer to String - <strong>my own fastest version</strong>
  
  <ul>
  <li>2502502 results / sec</li>
  </ul></li>
  <li>Same stuff with 2 buffers - only using <code>byte[]</code>, <code>getBytes()</code> and specifying encoding as "utf-8"
  
  <ul>
  <li>857485 results / sec</li>
  </ul></li>
  <li>Same stuff with 2 <code>byte[]</code> buffers, but specifying encoding as a constant <code>Charset.forName("utf-8")</code>
  
  <ul>
  <li>791076 results / sec</li>
  </ul></li>
  <li>Same stuff with 2 <code>byte[]</code> buffers, but specifying encoding as 1-byte local encoding (barely a sane thing to do)
  
  <ul>
  <li>370164 results / sec</li>
  </ul></li>
  </ul>
  
  <p>My best try was the following:</p>
  
  <pre><code>    char[] oldChars = new char[s.length()];
      s.getChars(0, s.length(), oldChars, 0);
      char[] newChars = new char[s.length()];
      int newLen = 0;
      for (int j = 0; j &lt; s.length(); j++) {
          char ch = oldChars[j];
          if (ch &gt;= ' ') {
              newChars[newLen] = ch;
              newLen++;
          }
      }
      s = new String(newChars, 0, newLen);
  </code></pre>
  
  <p>Any thoughts on how to make it even faster?</p>
  
  <p>Bonus points for answering a very strange question: why using "utf-8" charset name directly yields better performance than using pre-allocated static const <code>Charset.forName("utf-8")</code>?</p>
  
  <h1>Update</h1>
  
  <ul>
  <li>Suggestion from <em>ratchet freak</em> yields impressive 3105590 results / sec performance, a +24% improvement!</li>
  <li>Suggestion from <em>Ed Staub</em> yields yet another improvement - 3471017 results / sec, a +12% over previous best.</li>
  </ul>
  
  <h1>Update 2</h1>
  
  <p>I've tried my best to collected all the proposed solutions and its cross-mutations and published it as a <a href="https://github.com/GreyCat/java-string-benchmark" rel="noreferrer">small benchmarking framework at github</a>. Currently it sports 17 algorithms. One of them is "special" - <em>Voo1</em> algorithm (<a href="https://stackoverflow.com/questions/7161534/fastest-way-to-strip-all-non-printable-characters-from-a-java-string/7175771#7175771">provided by SO user Voo</a>) employs intricate reflection tricks thus achieving stellar speeds, but it messes up JVM strings' state, thus it's benchmarked separately.</p>
  
  <p>You're welcome to check it out and run it to determine results on your box. Here's a summary of results I've got on mine. It's specs:</p>
  
  <ul>
  <li>Debian sid</li>
  <li>Linux 2.6.39-2-amd64 (x86_64)</li>
  <li>Java installed from a package <code>sun-java6-jdk-6.24-1</code>, JVM identifies itself as
  
  <ul>
  <li>Java(TM) SE Runtime Environment (build 1.6.0_24-b07)</li>
  <li>Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)</li>
  </ul></li>
  </ul>
  
  <p>Different algorithms show ultimately different results given a different set of input data. I've ran a benchmark in 3 modes:</p>
  
  <h2>Same single string</h2>
  
  <p>This mode works on a same single string provided by <code>StringSource</code> class as a constant. The showdown is:</p>
  
  <pre>
   Ops / s  │ Algorithm
  ──────────┼──────────────────────────────
  6 535 947 │ Voo1
  ──────────┼──────────────────────────────
  5 350 454 │ RatchetFreak2EdStaub1GreyCat1
  5 249 343 │ EdStaub1
  5 002 501 │ EdStaub1GreyCat1
  4 859 086 │ ArrayOfCharFromStringCharAt
  4 295 532 │ RatchetFreak1
  4 045 307 │ ArrayOfCharFromArrayOfChar
  2 790 178 │ RatchetFreak2EdStaub1GreyCat2
  2 583 311 │ RatchetFreak2
  1 274 859 │ StringBuilderChar
  1 138 174 │ StringBuilderCodePoint
    994 727 │ ArrayOfByteUTF8String
    918 611 │ ArrayOfByteUTF8Const
    756 086 │ MatcherReplace
    598 945 │ StringReplaceAll
    460 045 │ ArrayOfByteWindows1251
  </pre>
  
  <p>In charted form:
  <a href="https://i.stack.imgur.com/5n8k5.png" rel="noreferrer"><img src="https://i.stack.imgur.com/5n8k5.png" alt="Same single string chart"></a><br>
  <sub>(source: <a href="http://www.greycat.ru/img/os-chart-single.png" rel="noreferrer">greycat.ru</a>)</sub>  </p>
  
  <h2>Multiple strings, 100% of strings contain control characters</h2>
  
  <p>Source string provider pre-generated lots of random strings using (0..127) character set - thus almost all strings contained at least one control character. Algorithms received strings from this pre-generated array in round-robin fashion.</p>
  
  <pre>
   Ops / s  │ Algorithm
  ──────────┼──────────────────────────────
  2 123 142 │ Voo1
  ──────────┼──────────────────────────────
  1 782 214 │ EdStaub1
  1 776 199 │ EdStaub1GreyCat1
  1 694 628 │ ArrayOfCharFromStringCharAt
  1 481 481 │ ArrayOfCharFromArrayOfChar
  1 460 067 │ RatchetFreak2EdStaub1GreyCat1
  1 438 435 │ RatchetFreak2EdStaub1GreyCat2
  1 366 494 │ RatchetFreak2
  1 349 710 │ RatchetFreak1
    893 176 │ ArrayOfByteUTF8String
    817 127 │ ArrayOfByteUTF8Const
    778 089 │ StringBuilderChar
    734 754 │ StringBuilderCodePoint
    377 829 │ ArrayOfByteWindows1251
    224 140 │ MatcherReplace
    211 104 │ StringReplaceAll
  </pre>
  
  <p>In charted form:
  <a href="https://i.stack.imgur.com/JogeX.png" rel="noreferrer"><img src="https://i.stack.imgur.com/JogeX.png" alt="Multiple strings, 100% concentration"></a><br>
  <sub>(source: <a href="http://www.greycat.ru/img/os-chart-multi100.png" rel="noreferrer">greycat.ru</a>)</sub>  </p>
  
  <h2>Multiple strings, 1% of strings contain control characters</h2>
  
  <p>Same as previous, but only 1% of strings was generated with control characters - other 99% was generated in using [32..127] character set, so they couldn't contain control characters at all. This synthetic load comes the closest to real world application of this algorithm at my place.</p>
  
  <pre>
   Ops / s  │ Algorithm
  ──────────┼──────────────────────────────
  3 711 952 │ Voo1
  ──────────┼──────────────────────────────
  2 851 440 │ EdStaub1GreyCat1
  2 455 796 │ EdStaub1
  2 426 007 │ ArrayOfCharFromStringCharAt
  2 347 969 │ RatchetFreak2EdStaub1GreyCat2
  2 242 152 │ RatchetFreak1
  2 171 553 │ ArrayOfCharFromArrayOfChar
  1 922 707 │ RatchetFreak2EdStaub1GreyCat1
  1 857 010 │ RatchetFreak2
  1 023 751 │ ArrayOfByteUTF8String
    939 055 │ StringBuilderChar
    907 194 │ ArrayOfByteUTF8Const
    841 963 │ StringBuilderCodePoint
    606 465 │ MatcherReplace
    501 555 │ StringReplaceAll
    381 185 │ ArrayOfByteWindows1251
  </pre>
  
  <p>In charted form:
  <a href="https://i.stack.imgur.com/TnQAd.png" rel="noreferrer"><img src="https://i.stack.imgur.com/TnQAd.png" alt="Multiple strings, 1% concentration"></a><br>
  <sub>(source: <a href="http://www.greycat.ru/img/os-chart-multi1.png" rel="noreferrer">greycat.ru</a>)</sub>  </p>
  
  <p>It's very hard for me to decide on who provided the best answer, but given the real-world application best solution was given/inspired by Ed Staub, I guess it would be fair to mark his answer. Thanks for all who took part in this, your input was very helpful and invaluable. Feel free to run the test suite on your box and propose even better solutions (working JNI solution, anyone?).</p>
  
  <h2>References</h2>
  
  <ul>
  <li><a href="https://github.com/GreyCat/java-string-benchmark" rel="noreferrer">GitHub repository</a> with a benchmarking suite</li>
  </ul>