contains() method is the method we use to determine whether a set contains an element or not. How does it work as a method provided by ArrayList.util? This blog will go deep into the underlying code of contains() method and explore its implementation principle.
Case 1: The existence of parameters
Example
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> nameList = new ArrayList(); nameList.add("Tom"); String name = "Tom"; System.out.println(namelist.contains(name)); } }
In the code snippet above, we created the collection nameList and added an element "Tom", then defined a string "Tom". Next, we used the "Ctrl + click contains method" to open the class where contains is located and analyze the underlying code (see code comment):
Underlying code
public boolean contains(Object o) {//Name is the incoming parameter, o points to the name object, and name is the up-transition object return indexOf(o) >= 0;//Call the following indexOf method } public int indexOf(Object o) {//o also points to name if (o == null) {//The string pointed to by name is not null, and the if statement is not executed for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++)//As you can see in the compiler, size is a global variable, hiding this (actually I < this. size) before it to get the number of elements contained in the collection nameList if (o.equals(elementData[i]))//Call the equals method in String type to see if each element in the collection is equal to the name one by one return i;//Equality returns a value greater than or equal to zero to the contains method above and finally returns true to determine the end. } return -1; }
Case 2: No parameter exists
Example
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> nameList = new ArrayList(); nameList.add("Tom"); String name = null; System.out.println(namelist.contains(name)); } }
In this case, the name string is null and the underlying code is analyzed as follows (see the code comment):
Underlying code
public boolean contains(Object o) {//Name is the incoming parameter, o points to the name object, and name is the up-transition object return indexOf(o) >= 0;//Call the following indexOf method } public int indexOf(Object o) {//o also points to name if (o == null) {//The string pointed to by name is null, and if statement is executed for (int i = 0; i < size; i++)//Traversing through all elements in a collection if (elementData[i]==null)//Determine whether an element exists as null return i;//Equality returns a value greater than or equal to zero to the contains method above and finally returns true to determine the end. } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
Note: The principle of using equals() method to judge whether two objects are equal can be referred to. Analysis of the underlying code of the equals() method
Case 3: Different types
Example
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> nameList = new ArrayList(); nameList.add("Tom"); Test test = new Test(); System.out.println(namelist.contains(test)); } }
In this case, we define a test object of type Test. If we still need to determine whether the set of generic String contains test, the result is false. The underlying code exploration principle is analyzed below (see the code annotations):
Underlying code
public boolean contains(Object o) {//Test is the input parameter, o points to the test object, and test is the up-transition object return indexOf(o) >= 0;//Call the following indexOf method } public int indexOf(Object o) {//o Also points to test if (o == null) {//test address is not empty and if statement is not executed for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++)//Traversing through all elements in a set to determine whether there are elements the same as test if (o.equals(elementData[i]))//The equals method is used to determine equality. Since the Test class does not override the equals method, it inherits the equals method of the parent class Object (comparison address). return i; } return -1;//If not, the contains method returns to - 1 value and finally returns false to judge the end. }
Case 4: Judging Custom Types
Example
First, we define a Student class and its parametric construction method.
public class Student { private String id; private String name; public Student(String id, String name) { this.id = id; this.name = name; } }
Next we create Student type objects and add Student type elements to the ArrayList collection
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Student> nameList = new ArrayList(); nameList.add(new Student("001","Tom"));//Adding Student type elements to the ArrayList collection Student student = new Student("001","Jim");//Create Student Type Objects System.out.println(namelist.contains(student)); } }
Underlying code
public boolean contains(Object o) {//Student is the input parameter, o points to the student object, and student is the up-transition object. return indexOf(o) >= 0;//Call the following indexOf method } public int indexOf(Object o) {//o Also points to student if (o == null) {//student address is not empty and if statement is not executed for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++)//Traversing through all elements in a collection if (o.equals(elementData[i]))//The object student pointed to by o is compared with the elements traversed by nameList. Since Student does not override the equals() method, it uses the equals method (comparison address) that inherits the parent Object. return i; } return -1;//If not, the contains method returns to - 1 value and finally returns false to judge the end. }
Consider: In the example of "Custom Type" above, even though the created Student-Type object and the Student-Type element string added to the ArrayList collection all have the same value, the final result of calling the contains method is still false, because the data type of the student object and studentList element above is false. All of them are our custom student types, so calling equals method in the bottom code of contains is not the equals method in our usual String class, but the equals method in the parent class Object is inherited by Student type. The equals method in Object only determines whether the addresses of the two objects are equal or not.
Then, if the equals method is overridden in the Student class, the effect of custom judgment can be achieved. The changes are as follows:
public class Student { private String id; private String name; public Student(String id, String name) { this.id = id; this.name = name; } // @Override // public boolean equals(Object obj) { // return super.equals(obj); // } //Above is the equals method that inherits Object by default @Override public boolean equals(Object obj) { Student student = (Student)obj;//Transform the elements we used earlier (the variable obj passed to the Object type as a parameter) down return this.id.equals(student.id);//This represents the object o (pointing to name) that calls this method; id is a String type, calling the equals method in the String type to determine whether the id of the element is the same. } }
The above code realizes the id attribute of the comparative custom class Student by rewriting equals() method. Of course, we can also rewrite equals to achieve the name attribute of the comparative Student class.