Hash code for any Apex type via System.hashCode

If you are writing your own class that you want to use in sets or as a map key, you must add equals and hashCode methods as described in Using Custom Types in Map Keys and Sets. But hashCode is usually implemented by combining the hash codes of the fields of the class and until now not all Apex types exposed a hash code value.

This is now addressed in Summer ’14 by the System.hashCode method. It saves a lot of work for SObjects and provides consistency for primitive types.

This test illustrates the method working:

@isTest
private class HashCodeTest {

    @isTest
    static void string() {
        // Already had a hashCode method
        System.assertEquals('hello world'.hashCode(), System.hashCode('hello world'));
    }
        
    @isTest
    static void decimal() {
        // Wasn't available before
        System.assertEquals(3843600, System.hashCode(123.987));
        System.assertEquals(3843600, System.hashCode(123.987));
        System.assertEquals(0, System.hashCode(0));
        System.assertEquals(1, System.hashCode(1));
        System.assertEquals(2, System.hashCode(2));
        System.assertEquals(1024, System.hashCode(3.3));
        System.assertEquals(-720412809, System.hashCode(4773463427.34354345));
    }
    
    @isTest
    static void sobject() {
        // Wasn't available before
        System.assertEquals(-1394890885, System.hashCode(new Contact(
                LastName = 'Doe')));
        System.assertEquals(-1394890885, System.hashCode(new Contact(
                LastName = 'Doe')));
        System.assertEquals(-1474401918, System.hashCode(new Contact(
                LastName = 'Smith')));
        System.assertEquals(744078320, System.hashCode(new Contact(
                LastName = 'Doe', FirstName = 'Jane')));
        System.assertEquals(744095627, System.hashCode(new Contact(
                LastName = 'Doe', FirstName = 'John')));
    }
    
    @isTest
    static void testNull() {
        try {
            System.assertEquals(0, System.hashCode(null));
            System.assert(false);
        } catch (NullPointerException e) {
        }
    }
}

Thanks to Daniel Ballinger for flagging this via a comment on Expose hashCode on all Apex primitives so hashCode viable for custom classes.

How to pass a large number of selections from a standard list view to a Visualforce page

Buttons can be defined and added to an object’s standard list view and these buttons can access the selected objects:

listview

If only a few items are selected then the IDs can be passed on to a Visualforce page like this (a “List Button” with “Display Checkboxes” checked that has behavior “Execute JavaScript” and content source “OnClickJavaScript”):

var ids = {!GETRECORDIDS($ObjectType.Contact)};
if (ids.length) {
    if (ids.length <= 100) {
        window.location = '/apex/Target?ids=' + ids.join(',');
    } else {
        alert('Select 100 or less');
    }
} else {
    alert('Select one or more Contacts');
}

The limit of 100 selected items is imposed because 2k characters is generally considered the longest URL that works safely everywhere, and a GET requires that each 15 character ID is appended to the URL together with a delimiter (to pass the values).

So what if you need to support more than 100 selected objects? (The standard list view UI does allow selections to be made on multiple pages and allows a single page to show up to 200 objects.) Using a POST instead of a GET where the ID values are part of the request body rather than the URL avoids the URL length problem. Here is how to do that:

var ids = {!GETRECORDIDS($ObjectType.Contact)};
if (ids.length) {
    var form = document.createElement("form");
    form.setAttribute("method", "POST");
    form.setAttribute("action", "https://c.na15.visual.force.com/apex/Target");
    var hiddenField = document.createElement("input");
    hiddenField.setAttribute("type", "hidden");
    hiddenField.setAttribute("name", "ids");
    hiddenField.setAttribute("value", ids.join(','));
    form.appendChild(hiddenField);
    document.body.appendChild(form);
    form.submit();
} else {
    alert('Select one or more Contacts');
}

This JavaScript creates a form and posts it to the server. An important part of making this work is that the full target URL needs to be specified as described in this Get POST data via visualforce page article otherwise the Apex controller can’t pickup the posted data via ApexPages.currentPage().getParameters(). An obscure trick to say the least.

This page:

<apex:page controller="Target">
    <h1>Target</h1>
    <apex:repeat value="{!ids}" var="id">
        <div>{!id}</div>
    </apex:repeat>
</apex:page>

and controller:

public with sharing class Target {
    public String[] ids {
        get {
            if (ids == null) {
                String s = ApexPages.currentPage().getParameters().get('ids');
                if (s != null) {
                    ids = s.split(',');
                } else {
                    ids = new String[] {};
                }
            }
            return ids;
        }
        private set;
    }
}

can be used to demonstrate that the IDs are passed correctly for both versions of the JavaScript button.