Check if ArrayList Contains String

I know this sounds really simple and it should be, but I can’t figure out what the problem is.

I have an ArrayList of strings, such that the 627th element is "

AESTHETICS

" exactly as printed out on the console (using

arraylist.get(627)

).

When I use arraylist.contains(“AESTHETICS”), it returns false. Using this method, it also returns false:

boolean dictContains(String s) {
		for(String str: arraylist) {
			if(str.equalsIgnoreCase(s)) {
				return true;
			}
		}
		
		return false;
		
	}

Can someone please point out what the problem is?

Strings are immutable, when you are doing “AESTHETICS”, the JVM is creating a new String object. You have to iterate all the objects in the list, and check using the [icode]equals[/icode] method.


boolean contains = false;

arraylist.forEach((s) -> contains = contains || s.equals("AESTHETICS"));

That should solve the problem.

SHC: you’re severely misinformed :point:


		List<String> dict = new ArrayList<>();

		dict.add("WURD");
		dict.add("WORD");
		dict.add("AESTHETICS");
		dict.add("WERD");

		System.out.println(dict.contains("AESTHETICS")); // returns true
		System.out.println(dict.contains(new String("AESTHETICS"))); // returns true

Oh my, that was true. I just thought that it is because strings are immutable. Could you also please explain why it was not working in the OP’s code? I think he is also doing the same.

I’m completely stumped now… :clue: ???

Hey, thanks for the replies

I still don’t understand how to fix it though, because I thought I was already checking using equals (equalsIgnoreCase). Can you please explain?

Perhaps the string is actually "AESTHETICS ", ie includes whitespace. Print to console including quotes to see this. Or its “AESTHETICS\r” resulting from splitting a windows text file by ‘\n’.

That was a good idea, turns out there was a new line at the end of the word. Printing with quotes gave

"AESTHETICS
"

Thank you all :slight_smile:

All strings in the constant pool of a class file, which include all explictly defined strings are (currently) interned at class resolution time. So currently the reference will return true for == thoughout a VM. This was a really bad idea and they’re thinking of making it go away.

This is highly confusing, given that Oracle just added String deduplication to the JVM. It pretty much acts like a GC, scanning the heap for duplicate Strings, rewriting references in case it found duplicates, so that after a while,


String s1 = new String("abc");
String s2 = new String("abc");
while(s1 != s2) {
   continue;
}
System.out.println("deduplicated");

will break from the loop.

If explicitly defined Strings are no longer a single ‘interned’ String anymore (AKA duplication), the deduplication feature will make them a single String after a while anyway.

On a side note: I believed that in recent Oracle JVMs the concept of ‘interned’ is deprecated/void anyway, which was possible due to the aforementioned deduplication feature.

Are you sure that’s how deduplication works?

I thought that the only thing that was overwritten was the private char array inside a String instance.

So the String instances would still be !=, it’s just that their char arrays would be ==.

(source)

Hm. Hm.

Well, once Strings and their char[]s are fused (future JREs) my point would stand :emo:

The loop WILL terminate, just maybe not for the next half decade…

I am a little confused here.

Is the OP checking to see if a given instance of String is inside the array? or if there is a String inside the array that has the same character sequence?

If the latter could you not just check if String A has the same character sequence as String B and ignore all case?

Is that what this does?:


boolean dictContains(String s) {
      for(String str: arraylist) {
         if(str.equalsIgnoreCase(s)) {
            return true;
         }
      }
      
      return false;
      
   }

Or is this actually calling .equals inside the method after lowering the case of the given string? Not sure what is happening here :S.

He’s checking for the existence of the same character sequence, disregarding case. The code is correct, but his data wasn’t trim()'d and had trailing whitespace that he didn’t notice.

Different issue. “abc” is a single instance in the constant pool. If another class also has “abc” in it they will (currently) be interned and have identical references. The code above is explicitly making two duplicates via new. So we now have (at least) three copies on heap. The deduplication feature will (at some point) reduce the number.

While we’re talking about this…one big problem with string identity is that it’s part of the spec and because of that some large corp codebases have “clever” constructions like: [icode]synchronized(“abc”)[/icode] but then again, [icode]new String(“abc”)[/icode] is equally clever. :stuck_out_tongue_winking_eye:

This is exactly what I meant to… convey :slight_smile:

Gotcha.

Definitely go buy that book: http://www.javapuzzlers.com/
It is hilarious! The best Java book I read in, …like…, all times. ;D
It contains all those weird “corner-cases” of Java and makes you laugh out loud when reading the solution to some of those puzzles.