ArrayList implementation
Probably the best improvement would be to allocate extra storage space and also store how many elements your list contains. Right now you allocate a new array and copy all existing values on each add operation, and this results in O(n) time complexity for adding items. Instead you could increase inner array twice each time, this would result in amortized O(1). Also, with this approach you wouldn't need to allocate new arrays on removal operations, as you could just leave empty space at the end.