code: @SuppressWarnings("unchecked")
    public void update(final Sketch<S> a, final Sketch<S> b) {
      if (a != null) { isEmpty_ = a.isEmpty(); } //stays this way even if we end up with no result entries
      final long thetaA = a == null ? Long.MAX_VALUE : a.getThetaLong();
      final long thetaB = b == null ? Long.MAX_VALUE : b.getThetaLong();
      theta_ = Math.min(thetaA, thetaB);
      if (a == null || a.getRetainedEntries() == 0) { return; }
      if (b == null || b.getRetainedEntries() == 0) {
        getNoMatchSetFromSketch(a);
      } else {
        final long[] hashTable;
        if (b instanceof CompactSketch) {
          hashTable = convertToHashTable(b);
        } else {
          hashTable = b.keys_;
        }
        final int lgHashTableSize = Integer.numberOfTrailingZeros(hashTable.length);
        final int noMatchSize = a.getRetainedEntries();
        keys_ = new long[noMatchSize];
        summaries_ = (S[]) Array.newInstance(a.summaries_.getClass().getComponentType(), noMatchSize);
        for (int i = 0; i < a.keys_.length; i++) {
          if (a.keys_[i] != 0) {
            final int index = HashOperations.hashSearch(hashTable, lgHashTableSize, a.keys_[i]);
            if (index == -1) {
              keys_[count_] = a.keys_[i];
              summaries_[count_] = a.summaries_[i];
              count_++;
            }
          }
        }
      }
    }
docs: Perform A-and-not-B set operation on the two given sketches.
  A null sketch is interpreted as an empty sketch.
  This is not an accumulating update. Calling update() more than once
  without calling getResult() will discard the result of previous update()
  
  @param a The incoming sketch for the first argument
  @param b The incoming sketch for the second argument