ArrayList.contains(Object o)
필자의 블로그 [커리어리 글을 작성하는 것이 다소 익숙하지 않기에, 아래 블로그로 가면 가독성이 더욱 좋을 것 입니다.] https://blog.naver.com/PostView.naver?blogId=gomets_journey&logNo=223319509902&parentCategoryNo=&categoryNo=28&viewDate=&isShowPopularPosts=false&from=postList ArrayList란 무엇인가? ArrayList는 List 인터페이스를 상속받은 구현체로 크기가 가변적으로 변하는 선형리스트이다. ArrayList의 특징 * 내부 저장소가 연속적인 메모리 형태이며 인덱스로 내부의 객체를 관리한다는 점에서 배열과 유사하다. * 한 번 생성되면 크기가 변하지 않은 배열과 달리 ArrayList는 객체들이 추가되어 저장 용량(capacity)를 초과한다면 자동으로 부족한 크기만큼 저장 용량이 늘어난다. * 데이터 중복이 가능하며, null 값도 허용한다. * 자료를 대량으로 추가하거나 삭제하면 내부 처리 작업이 늘어나서 성능이 떨어질 수 있다. ArrayList.contains(Object o)의 내부코드 public boolean contains(Object o) { return indexOf(o) >= 0; } 이 코드를 보면 indexOf()메서드의 파라미터 o를 매개변수로 할당하여 그 값이 0이상일 경우 true를 반환한다는 것을 알 수 있다. 그러면 indexOf()메서드는 무엇일까? ArraysList.indexOf(Object o)의 내부코드 public int indexOf(Object o) { return indexOfRange(o, 0, size); } 이 코드만 봐서는 우리는 indexOfRange()라는 메서드를 이용하는데 o, 0, size를 파라미터를 이용하여 어떠한 값을 반환한다는 것만 알 수 있다. 즉 indexOf()를 이해하기 위해서 indexOfRange()메서드를 살펴보자 ArraysList.indexOfRange(Object o, int start, int end)의 내부코드 int indexOfRange(Object o, int start, int end) { Object[] es = elementData; if (o == null) { for (int i = start; i ) o) : equalsRange((List) o, 0, size); checkForComodification(expectedModCount); return equal; } 1. if (o == this) : 먼저, 객체가 자기 자신과 같은지 확인한다. 자기 자신과 같으면 true를 반환한다. 2. if (!(o instanceof List)) : 다음으로, 객체가 자기 자신과 같은지 확인한다. (그 하위 클래스 타입도 해당도니다.) 만약 List의 인스턴스가 아니면 false를 반환한다. 3. final int expectedModCount = modCount : 이 부분은 현재 겍체의 modCount 값을 저장한다. modCount는 리스트가 변경될 때마다 증가하는 수정 횟수를 나타내는 변수로, 후에 수정 여부를 확인하는 데 사용된다. 아래는 modCount를 활용한 예제 코드이다. import java.util.ArrayList; import java.util.List; import java.util.ConcurrentModificationException; public class ExampleArrayList { private transient int modCount = 0; // Example modCount variable in ArrayList public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof List)) { return false; } final int expectedModCount = modCount; // Save the current modCount // Simulate a concurrent modification by incrementing modCount modCount++; // ArrayList can be subclassed and given arbitrary behavior, but we can // still deal with the common case where o is ArrayList precisely boolean equal = (o.getClass() == ArrayList.class) ? equalsArrayList((ArrayList) o) : equalsRange((List) o, 0, size()); // Check for concurrent modification checkForComodification(expectedModCount); return equal; } private boolean equalsArrayList(ArrayList other) { // Implementation for comparing two ArrayLists // ... return true; } private boolean equalsRange(List other, int start, int end) { // Implementation for comparing a range of elements in the ArrayList with another List // ... return true; } private void checkForComodification(int expectedModCount) { // Check if the list has been concurrently modified if (modCount != expectedModCount) { throw new ConcurrentModificationException("List has been modified concurrently"); } } private int size() { // Implementation for getting the size of the ArrayList // ... return 0; } public static void main(String[] args) { ExampleArrayList exampleList = new ExampleArrayList(); // Usage of the equals method boolean result = exampleList.equals(new ArrayList()); System.out.println("Equality check result: " + result); } } 이 코드에서는 modCount를 임의로 증가시켜서 동시 수정을 시뮬레이션하고, checkForComodification 메서드를 사용하여 동시 수정 여부를 확인하는 기본적인 로직을 구현했다. 이는 ArrayList의 equals 메서드에서 실제로 발생하지 않지만, 실제 ArrayList에서는 이러한 메커니즘을 사용하여 동시 수정을 감지한다. 4. boolean equal = (o.getClass() == ArrayList.class) ? equalsArrayList((ArrayList o) : equalsRange((List) o, 0, size) : 여기서는 o가 ArrayList 타입인지 확인한다. 만약 ArrayList 타입이라면 equalsArrayList 메서드를 호출하고 그렇지 않으면 equalsRange 메서드를 호출하여 리스트의 특정 범위 내에서의 동등성을 확인한다. (이는 ArrayList가 일반적인 경우에도 안전하게 동작함을 보장한다. 즉, 서브클래스에서도 말이다.) 5. checkForComodification(expectedModCount) : 이 부분은 이전에 지정한 expectedModCount 값과 현재 리스트의 modCount 값을 비교하여 리스트가 동시 수정되었는지 확인하는 메서드이다. 동시 수정이 감지되면 ConcurrentModificationException을 던진다. 6. 마지막으로, equal 값을 반환한다. 즉, 두 리스트가 동등하면 true를 반환하고, 그렇지 않으면 false를 반환한다. 5번의 내용이 잘 이해가 안되니 checkForComodification(expectedModCount)의 내부 코드를 살펴보자. checkForComodification(expectedModCount)의 내부 코드 private void checkForComodification(final int expectedModCount) { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } checkForComodification 메서드는 리스트의 수정 여부를 확인하고, 동시에 여러 스레드에서 리스트를 수정하려는 시도가 있었는지를 검사하는 역할을 한다. 즉, equals 메서드 내에서 동시에 다른 쓰레드에서 리스트를 수정하는 경우에 예외를 발생시켜서 이를 방어하는 역할을 하는 것이다. checkForComodification 메서드가 사용된 다른 메서드는 없는지 살펴보니 next 메서드와 remove 메서드를 발견했다. next 메서드의 내부 코드를 살펴보자 ArrayList.next()의 내부 코드 public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } ArrayList.remove()의 내부 코드 public void remove() { if (lastRet i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; } fastRemove 메서드는 modCount++로 modCount 값을 증가시키고 있는 것을 확인할 수 있다. 즉, 정리하자면 Collection 구현체를 Iterator로 순환시 remove하게되면 Collection이 변경되었다고 판단하여 예외를 던진다. 내가 앞서 Iterator를 학습하였을 때는 순환 시 삭제가 가능하다고 알고있었다. 하지만 Iterator는 사실 순환 중 변경을 금지하고 있었다는 것을 알게되었다. 그저 동등한지를 판단하기위해 써왔던 equals 메서드에서 생각지 못하게 많은 부분을 학습하고 오지식을 제대로 다시 고칠 수 있게되었다.