The DynamicArray Trap
When the elements were Students, our two overloads, remove(T value) and remove(int index), were easy to tell apart. Now make the element type Integer. The two overloads become remove(Integer) and remove(int):
DynamicArray<Integer> scores = new DynamicArray<>();
scores.add(1);
scores.add(2);
scores.add(3);
scores.remove(2); // which remove runs?
You might expect this to remove the value 2. It does not. It removes the element at index 2, which is the value 3.
Why Java chooses the index overload
The argument 2 is a plain int, and both methods can accept it. remove(int) takes it as it is. remove(Integer) takes it after autoboxing, since Java will box an int into an Integer for you.
Java has a fixed rule for this case: it prefers the overload that needs no conversion. Matching remove(int) requires nothing of the argument, and matching remove(Integer) requires a boxing step first, so Java chooses remove(int). Notice that the compiler decides this from the type of the argument you wrote. It has no way to know what you meant.
To remove the value 2, you have to pass remove an actual Integer, so that remove(T) is the only overload the call fits:
scores.remove(Integer.valueOf(2)); // remove(T): delete the value 2
When two overloads differ only by int versus a wrapper type, a plain integer literal always picks the int one. Write Integer.valueOf(...) when you mean the value.