Binary Search vs Contains Performance in Java List


When it comes to searching for elements in a collection, Java provides different options depending on the data structure you're using. Two popular methods for searching in a list are binary search and the contains() method. In this blog post, we'll compare the performance of binary search and contains in a Java list, highlighting their differences, strengths, and best use cases.

Binary Search

Binary search is an efficient way for locating a specific member in a sorted list. The search space is divided in half on a regular basis until the target element is found or the search space is exhausted in a divide−and−conquer method. Binary search assumes that the list has already been sorted and compares the target element with the centre element of the current search area to determine whether to move on to the left or right half of the search area.

This algorithm has a time complexity of O(log n), where n is the number of elements in the list. Binary search is highly efficient for large sorted lists as it eliminates half of the remaining search space in each iteration which reduces the number of comparisons.

The contains() method

The contains() method is a quick approach to determine whether a list contains a particular member. Until a match is found or the end of the list is reached, it repeatedly goes through each element in the list and compares it to the target element. The includes method has an O(n) time complexity, where n is the number of entries in the list, and it does not require that the list be sorted. This indicates that the amount of time needed to find an element increases linearly as the list size increases.

Example

Here is the program that compares the performance time between the contains method and binary search for finding the number 60 in a sorted list containing 100 elements from 0 to 149:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HelloWorld {

    public static void main(String[] args) {
        // Creating a sorted list of integers from 0 to 99
        List<Integer> sortedList = new ArrayList<>();
        for (int i = 0; i < 150; i++) {
            sortedList.add(i);
        }

        // Searching for number 40 using binary search
        long startTimeBinarySearch = System.nanoTime();
        int indexBinarySearch = Collections.binarySearch(sortedList, 60);
        long endTimeBinarySearch = System.nanoTime();
        long executionTimeBinarySearch = endTimeBinarySearch - startTimeBinarySearch;

        System.out.println("Binary Search Result: Index " + indexBinarySearch);
        System.out.println("Binary Search Execution Time: " + executionTimeBinarySearch + " nanoseconds");

        // Searching for number 40 using contains
        long startTimeContains = System.nanoTime();
        boolean foundContains = sortedList.contains(40);
        long endTimeContains = System.nanoTime();
        long executionTimeContains = endTimeContains - startTimeContains;

        System.out.println("Contains Result: " + foundContains);
        System.out.println("Contains Execution Time: " + executionTimeContains + " nanoseconds");
    }
}

Output

Binary Search Result: Index 60
Binary Search Execution Time: 23805 nanoseconds
Contains Result: true
Contains Execution Time: 12073 nanoseconds

Example

Here's the code to create an unsorted ArrayList containing random numbers between 0 and 100,000. We'll compare the performance of –

  • The contains() method without sorting.

  • The Collections.binarySearch() method after sorting the list.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class HelloWorld {
    public static void main(String[] args) {
        // Create an unsorted list of random numbers
        List<Integer> unsortedList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 1000000; i++) {
            unsortedList.add(random.nextInt(100001));
        }
        // Searching for number 50000 using contains without sorting
        long startTimeContains = System.nanoTime();
        boolean foundContains = unsortedList.contains(50000);
        long endTimeContains = System.nanoTime();
        long executionTimeContains = endTimeContains - startTimeContains;

        System.out.println("Contains Result (Unsorted List): " + foundContains);
        System.out.println("Contains Execution Time (Unsorted List): " + executionTimeContains + " nanoseconds");

        // Sorting the list
        Collections.sort(unsortedList);

        // Searching for number 50000 using binary search after sorting
        long startTimeBinarySearch = System.nanoTime();
        int indexBinarySearch = Collections.binarySearch(unsortedList, 50000);
        long endTimeBinarySearch = System.nanoTime();
        long executionTimeBinarySearch = endTimeBinarySearch - startTimeBinarySearch;

        System.out.println("Binary Search Result (Sorted List): Index " + indexBinarySearch);
        System.out.println("Binary Search Execution Time (Sorted List): " + executionTimeBinarySearch + " nanoseconds");
    }
}

Output

Contains Result (Unsorted List): true
Contains Execution Time (Unsorted List): 2092909 nanoseconds
Binary Search Result (Sorted List): Index 499734
Binary Search Execution Time (Sorted List): 22943 nanoseconds

Performance comparison

The performance of binary search and contains() method can vary depending on the characteristics of the list and the specific use case. Here are some factors to consider when choosing between the two:

  • List Size: Binary search performs best on large sorted lists, while the contains() method works well for smaller unsorted lists. As the size of the list increases, the advantage of binary search becomes more pronounced due to its logarithmic time complexity.

  • Sorting Overhead: Binary search requires the list to be sorted initially or after each modification, which introduces additional overhead. If the list is frequently modified and sorting is expensive, using the contains method might be more efficient.

  • Memory Overhead: Binary search operates on the original list without creating any additional data structures. In contrast, contains creates an iterator or uses the list's iterator internally, which introduces some memory overhead.

  • Element Equality: Binary search relies on the comparison of elements using the Comparable or Comparator interface. If the element type does not implement these interfaces, you must provide a custom comparator. On the other hand, contains uses the equals method to compare elements, which might be more straightforward to use in certain cases.

Use cases

To summarize, here are the recommended use cases for binary search and contains:

  • Binary search is best suited for large sorted lists when you need to find elements efficiently. It's particularly useful when the list is relatively static or when sorting overhead is acceptable.

  • Contains is suitable for smaller unsorted lists or cases where the list is frequently modified and sorting overhead is a concern. It's a simple and convenient method for searching when the list doesn't need to be sorted.

Conclusion

In conclusion, binary search and contains are two different approaches to searching for elements in a Java list. Binary search is highly efficient for large sorted lists, while contains is more suitable for smaller unsorted lists or when sorting overhead is a concern. Choosing the right method depends on the characteristics of your list and the specific requirements of your application.

Updated on: 12-Jul-2023

207 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements