Map.containsKey/Set.contains

by Mikhail Vorontsov

Both java.util.Map.containsKey and java.util.Set.contains methods should not be often used in your code. Their functionality is covered by other Map/Set methods which you are likely to use after containsKey/contains call.

Sets

If you want to check if you have some key in your set, and, if it is not present, add it and do something else, you may write code like this:

1
2
3
4
5
if ( !set.contains( key ) )
{
    set.add( key );
    //some extra code here
}
if ( !set.contains( key ) )
{
    set.add( key );
    //some extra code here
}

Instead, it will be faster to use check from add method itself. It will return true if a given key was not present in the set before (you can treat true as “did something” and false as “did nothing”).

1
2
3
4
if ( set.add( key ) )
{
    //same extra code could be added here
}
if ( set.add( key ) )
{
    //same extra code could be added here
}

remove method works absolutely similar: it returns true if it had removed given key (it is the same again: true for “did something”, false for “did nothing”). So, the following code

1
2
3
4
5
if ( set.contains( key ) )
{
    set.remove( key );
    //some extra code here
}
if ( set.contains( key ) )
{
    set.remove( key );
    //some extra code here
}

shall be replaced with faster one:

1
2
3
4
if ( set.remove( key ) )
{
    //same extra code here
}
if ( set.remove( key ) )
{
    //same extra code here
}

Maps

The ugliest pair of methods is Map.containsKey followed by Map.get, which is usually chained with some other method call, like map.get( key ).size(). In this case contains works as implicit defence against NullPointerException, which may be thrown if given key is not present in the map (Map.get will return null and chained method call will fail).

1
2
3
4
5
6
7
8
final Map<String, String> map = new HashMap<String, String>( 10 );
map.put( "a", "A" );
//later in the code
final String key = "a";
if ( map.containsKey( key ) )
{
    System.out.println( map.get( key ).length() );
}
final Map<String, String> map = new HashMap<String, String>( 10 );
map.put( "a", "A" );
//later in the code
final String key = "a";
if ( map.containsKey( key ) )
{
    System.out.println( map.get( key ).length() );
}

If will be faster to get value first and then check if it is null, which is equivalent to containsKey call:

1
2
3
4
5
6
7
8
9
final Map<String, String> map = new HashMap<String, String>( 10 );
map.put( "a", "A" );
//later in the code
final String key = "a";
final String value = map.get( key );
if ( value != null )
{
    System.out.println( value.length() );
}
final Map<String, String> map = new HashMap<String, String>( 10 );
map.put( "a", "A" );
//later in the code
final String key = "a";
final String value = map.get( key );
if ( value != null )
{
    System.out.println( value.length() );
}

remove logic described for sets is applicable to the same java.util.Map method too.

Summary

For sets, contains+add/remove call pairs should be replaced with single add/remove calls even if some extra logic was guarded by contains call.

For maps, contains+get pair shall always be replaced with get followed by null-check of get result. contains+remove pair should be replaced with a single remove call and check of its result.

Same ideas are applicable to Trove maps and sets too.


Leave a Reply

Your email address will not be published. Required fields are marked *