Collapsing multiple ints into a long

Every now and then, you may want to collapse multiple values into a long, to use as a key for a Map, for example.
The following code is an example of collapsing three ints into one long. Obvious care should be taken here: three ints don’t actually fit into one long, so the excess bits are just discarded. If we do this, we can afford to use the lowest 20 bits, plus the sign bit, leaving us with a data range of 1,048,576 to -1,048,575.
For each int we take the lower 20 bits:

  input & 0xfffff

…and the sign bit, which we move to position 21:

  (input & Integer.MIN_VALUE) >>> 11

and then combine the two:

  (input & 0xfffff | (input & Integer.MIN_VALUE) >>> 11)

Now, we repeat the process for all our inputs and put them in our long, shifting two of them by 21 and 42 respectively so they don’t overlap:

  long l = 0;
  l |= (long)(input1 & 0xfffff | (input1 & Integer.MIN_VALUE) >>> 11);
  l |= (long)(input2 & 0xfffff | (input2 & Integer.MIN_VALUE) >>> 11) << 21;
  l |= (long)(input3 & 0xfffff | (input3 & Integer.MIN_VALUE) >>> 11) << 42;

If you were to combined a higher amount of things with less bits, this loop version will probably be more concise, courtesy of Maldivia:

  public static long pack(int i1, int i2, int i3) {
    int[] data = new int[] { i1, i2, i3 };
    long l = 0;
    for (int i = 0; i < 3; i++) {
       l |= (long)(data[i] & 0xfffff | (data[i] & Integer.MIN_VALUE) >>> 11) << (i * 21);
    }
    return l;
  }

And to unpack the long back into pieces:

  public static int[] unpack(long l) {
    int[] res = new int[3];
    for (int i = 0; i < 3; i++) {
       res[i] = (int) (l & 0xfffff);
       if ((l & 0x100000) != 0) {
         res[i] |= 0xfff00000;
       }
       l >>>= 21;
    }
    return res;
  }