But IMO the most common case (certainly at least one of the usages I've written already) is going to be two elements where one is likely to be NULL:

  void addMyHook(ReceivePack rp) {
    List<PostReceiveHook> newHooks = new ArrayList<PostReceiveHook>();
    newHooks.add(rp.getPostReceiveHook());
    newHooks.add(new MyHook());
    rp.setPostReceiveHook(PostReceiveHookChain.newChain(newHooks);
  }

In this case, if no previous hook was set, then the 0- and 1-element optimizations don't apply unless you also collapse NULLs.