2

I understand that the equality operator compares references to the strings. So, it will check to see if the strings refer to the same object and not if they are equal character by character.

As a first step in learning about search algorithms, I set up the following program where I have an array of names and then I check if a certain name appears in the array.

First Approach :

I declare and initialize the array of the names. And I ask the user to input a name to check if it appears in the array.

Here's the code I used -

import java.util.Scanner;

public class Strawman{

    public static void main(String[] args){

        System.out.println("Enter the name to search for:");

        Scanner scanner = new Scanner(System.in);
        String key = scanner.nextLine();

        String[] names = {"alice", "bob", "carlos", "carol", "craig", "dave", "erin", "eve", "frank", "mallory", "oscar", "peggy", "trent", "walter", "wendy"};

        for (int i = 0; i < names.length; i++){

            if (key == names[i]) {
                System.out.println("Index " + i + " has the name " + key);
            } 
        }
    }

}

One of the runs of this program is shown in the following screenshot - program output

As expected, because I'm using the == operator to compare strings, this fails to find the name "oscar" in the array, even though it appeared in the initial array. This output is as expected based on my understanding of how equality operators compares references of the strings.

But, I don't understand why the program seems to work if instead of asking for user input, I declare the name to search for as a string.

Second Approach:

The name "oscar" to search for has been declared as a string instead of asking for user input -

public class Strawman2{

    public static void main(String[] args){

        String[] names = {"alice", "bob", "carol", "craig", "carlos", "dave", "eve", "fred", "greg", "gregory", "oscar", "peter"};
        String key = "oscar";

        for (int i = 0; i < names.length; i++){

            if (names[i] == key){
                System.out.println("Index " + i + " has name " + key);
            }

        }

    }

}

Now, if I run the program, the name "oscar" is found in the array - output_approach2

Can someone explain the difference in the two cases?

Ratus
  • 107
  • 1
  • 6

3 Answers3

6

It's because in the second approach

String key = "oscar";

reuses an instance from the string constant pool populated by

String[] names = {"alice", "bob", "carol", "craig", "carlos", "dave", "eve", "fred", "greg", "gregory", "oscar", "peter"};

Change the way you initiate key variable into:

String key = new String("oscar");

it will behave the same way as first approach as you bypass the String Constant Pool and your key variable will now refer to another object in memory.

For more information about the String Constant Pool: String Constant Pool

Allan
  • 12,117
  • 3
  • 27
  • 51
2

It's because the compiler reuses string instances from string literals that are known at compile time. Hence they pass the object equality check. Reuse is possible because Strings are immutable objects.

Strings that are not know at compile time, and/or explicitly created as new String objects, are not subject to this optimisation and will always result in new objects.

ig-dev
  • 489
  • 1
  • 5
  • 15
1

There are only two situations where == is guaranteed to work (as you want) for string testing:

  1. You created a String object explicitly and know sure that you are using the same reference for it in two different places.

  2. You know for sure that both strings you are comparing have been interned. Noting that string literals are always1 interned.

    Technically, an interned string is one that is a result of calling String::intern some time during its lifetime. (See JLS 3.10.5 and the javadoc.) Informally an interned string is one that "is in the String pool", though the term "the String pool" is not specified anywhere2.

Anything else and == is liable going to give the wrong answer.

And ... those two cases rarely arise in real-world programs.


1 - Not strictly 100% true: consider literals that are subexpressions in constant expressions. However, that does not affect the behavior of the == operator.

2 - The closest I have found is "a pool of strings, initially empty, is maintained privately by the class String" in the javadocs. But the current javadocs, JLS and JVM spec don't use the phrase "the String pool" or "the String constant pool" or any of the other variations any place that I can find.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216