16

So the question here is pretty simple: is there a way to tell if a String in Java is interned? My guess is no, but I'm wondering if anyone knows better.

Masterofpsi
  • 769
  • 6
  • 17
  • 3
    I wonder *why* do you need this information? If it's for optimization to avoid unnecessary call to intern I'd say don't do it and just intern away.. – Mikko Wilkman Feb 03 '11 at 08:35

4 Answers4

16

The only way you can find out if a String is interned is to call intern() and check if it returns itself:

boolean hasBeenInternedBefore = myString.intern() == myString;

This obviously has the drawback of interning the String when it wasn't interned before.

Going partially off-topic, there's a way to do "custom" interning with an explicit pool using the Interner interface of Guava (using the implementations exposed by the Interners class). This has the advantage of being able to let the Interner itself (and thuse the pool) being garbage collected when it's no longer referenced.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • Didn't though about that way. It seems like an equivalent of destructive test in forensics. – Riduidel Feb 03 '11 at 08:32
  • 6
    @Riduidel At least you get to know if the cat *was* alive or not :-) –  Feb 03 '11 at 08:33
  • 1
    How about `hasBeenInternedBefore = myString == new String(myString).intern()`? – aioobe Feb 03 '11 at 08:33
  • @aioobe Any time `intern` is called it interns the string -- the intern'ed value is then separate from the initial strings lifetime. Unless I'm missing something the withe explicit ctor. –  Feb 03 '11 at 08:35
  • @aioobe: I don't see the advantage in that. My version doesn't *change* `myString` either, it only places a copy of it into the intered `String` pool. In those effects your version is equivalent, but it always creates one more `String` object. – Joachim Sauer Feb 03 '11 at 08:36
  • @Joachim, I don't see the advantage either. It was just a reply to your "The drawback of interning the String ..." – aioobe Feb 03 '11 at 08:37
  • @pst - no, you can say, that the cat is dead *now*. The test may kill the cat before it really starts (just to stay in the picture) – Andreas Dolk Feb 03 '11 at 08:42
  • 1
    It's just like 'go outside and check if you are there'. If you call intern(), your String will be internalized anyway so you could return true anyway with that solution. – Danubian Sailor Feb 03 '11 at 09:02
  • 1
    @lechlukasz - not exactly, `myString.intern()` returns the reference of the interned String that is equal to `myString`. It does not change the stored reference in `myString`! So the result may be `false`. – Andreas Dolk Feb 03 '11 at 09:06
  • loving the schrodinger's cat references. – mR_fr0g Feb 03 '11 at 09:35
  • @Andread_D yes, it does not change the stored reference, but the content of the string will be interned after that call! – Danubian Sailor Feb 03 '11 at 09:54
  • @JoachimSauer: This test has unpleasant side effect: it will always intern `myString` thus blowing PermGen. So one can get OOM while using this "innocent" test. – dma_k Jun 28 '12 at 16:02
  • 1
    @dma_k: yes, and I even mention that very fact in my third paragraph. – Joachim Sauer Jun 28 '12 at 17:20
  • @JoachimSauer: Thanks for the link to Guava's `Interners`. It's a pity that `Interner` interface does not have `boolean isInterned(E)` method, which would be very relevant to the question. Although it's not difficult to implement it by oneself... – dma_k Jun 29 '12 at 09:41
  • @JoachimSauer hi i am new to java and i didn't understood the drawback(you mentioned in your answer) exactly. can you explain that in somewhat more clearly – JAVA Sep 10 '13 at 01:18
  • @sunny: the string pool (i.e. "where interned strings are stored") has traditionally been pretty permanent and has not been garbage collected. This has changed (I believe sometime in the Java 6 timeframe), so that it's now garbage collected just like any other object. But people are still reluctant to use it (probably because there is no way to tell if you're using a "new" or an "old" implementation). – Joachim Sauer Sep 10 '13 at 05:52
  • 1
    At least on Java 7, this returns false positives. `hasBeenInternedBefore` is `true` if the string's contents have never been interned before. `myString` is interned and returned as itself, so the equality holds. It only tests `contentsHaventBeenInternedBeforeAsAnotherStringObject`. – Dan Getz Jul 25 '15 at 17:36
  • @DanGetz indeed, this kind of test is entirely worthless. It only tells you, whether there is an interned string with the same contents and different identity. – Holger Jul 31 '18 at 14:10
3

There is a way to check if the particular String object was already interned, but it inserts the contents into the string pool if those contents weren't already interned. Create a new String object with the same contents, intern that, and compare to your original object:

new String(s).intern() == s

This works because of the fact that new String(s) != s. Consider each possible situation:

  1. s is interned in the string pool. new String(s) has the same contents as s, so intern() called on it will return s. The expression's result is true.
  2. s is not interned in the string pool, but another equal String object is—let's call it s2. intern() will return s2, so the expression's result is false.
  3. s is not interned in the string pool, and neither is any String equal to it. In this case, new String(s) will be interned into the string pool, which unfortunately modifies the string pool. Because this is not the same String object as s, the expression's result is false.

Thus the above expression will correctly test if s is interned in the string pool or not. The following test demonstrates this:

public static void main(String[] args) {
    String interned = new String(new char[] { 'i', 'n', 't' }).intern();
    String notInterned = new String(new char[] { 'n', 'o', 't' });
    System.out.println("Case 1: " + wasInterned(interned));
    System.out.println("Case 2: " + wasInterned(new String(interned)));
    System.out.println("Case 3: " + wasInterned(notInterned));
}

public static boolean wasInterned(String s) {
    return new String(s).intern() == s;
}

When run, the output is:

 Case 1: true
 Case 2: false
 Case 3: false
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
0

We can't look inside the repository of interned strings and can't get a set of all interned Strings.

Testing, if a given String has been interned already creates a nice dilemma (a common problem with measurements by the way): the measuring itself may affect the repository of interned Strings ;)

To test, if a repository contains a given String, we need to compare that String to (worst case) all Strings in that repository - with the risk, that JVM interns this reference String before we start comparing... which would return a true although the String wasn't interned before the test ;)

But apart from this, I don't see any practical use in knowing if the virtual machine has interned a String already or not. Interning is cheap enough, just intern it if needed. (And if there was a practical use, the String class would offer a native test method)

Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • Interning is cheap, but if an application handles an enormous amount of Strings, the memory consumption could become a problem I guess. – Adriaan Koster Feb 03 '11 at 09:21
  • AFAIK, there may be problems when too many strings get interned, probably because of them landing in the PermSpace. Consider using WeakInterner from Guava in such a case. – maaartinus Feb 03 '11 at 10:21
  • 1
    @maaartinus Even today, without the PermGen space, in the HotSpot JVM, the table of interned strings has a fixed size, a ridiculously small fixed size on older JVMs, which leads to lots of collisions, even when not using it exhaustively. Besides that, it implies modifying a globally visible data structure in a thread safe way, in other words, can never be a cheap operation at all. – Holger Aug 01 '18 at 13:07
0

No. Generally speaking, you shouldn't need to check - just intern them to be sure or don't rely on interning. If you need interned or non-interned strings for testing or experimenting, you can make them:

Interned:

s = someArbitraryString.intern();

Non-interned:

s = new String(someArbitraryString);
Bert F
  • 85,407
  • 12
  • 106
  • 123