Implementing contains, add, and remove

With indexOf in place, contains just reports whether the search found the item:

@Override
public boolean contains(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  return indexOf(item) != -1;
}

The add operation rejects a duplicate, grows the array if it is full, then appends the item at the end:

@Override
public boolean add(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  if (indexOf(item) != -1) {
    return false;    // already present; a set holds no duplicates
  }
  if (size == arr.length) {
    grow();          // grow when the array is full
  }
  arr[size] = item;  // append at the end
  size++;
  return true;
}

The grow helper is the doubling we wrote for ArrayList.

The remove operation finds the item, then moves the last item into the gap and clears the slot it left behind:

@Override
public boolean remove(T item) {
  if (item == null) {
    throw new IllegalArgumentException();
  }
  int i = indexOf(item);
  if (i == -1) {
    return false;          // not present; nothing changed
  }
  arr[i] = arr[size - 1];  // move the last item into the gap
  arr[size - 1] = null;    // clear the vacated slot
  size--;
  return true;
}