Programing

Java에서 유형 목록과 유형 ArrayList

lottogame 2020. 2. 11. 22:17
반응형

Java에서 유형 목록과 유형 ArrayList


(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

나는 (1)을 사용하여 List 인터페이스의 구현을 바꿀 수 있음을 이해합니다 . (1)은 일반적으로 필요에 관계없이 응용 프로그램에서 사용되는 것으로 보입니다 (자체는 항상 이것을 사용합니다).

누군가 (2)를 사용하는지 궁금합니다.

또한 상황이 실제로 (1) 이상 (2) (즉, (2) 충분하지 않은 인터페이스모범 사례 등을 제외 하고 )를 사용해야하는 상황은 얼마나 자주 (그리고 예제를 얻을 수 있습니까? )


거의 항상 첫 번째 것이 두 번째 것보다 선호됩니다. 첫 번째는 코드의 나머지 부분에 영향을 미치지 않고 구현을 List변경할 수 있다는 장점이 있습니다 LinkedList. 이가 함께 할 수있는 어려운 작업이 될 것입니다 ArrayList당신이 변화해야하므로뿐만 아니라 ArrayListLinkedList사방뿐만 아니라, 당신은 사용이있을 수 있으므로 ArrayList특정 방법을.

여기에서List 구현 에 대해 읽을 수 있습니다 . 으로 시작할 수 있지만 곧 다른 구현이 더 적합한 지 알게됩니다.ArrayList


누군가 (2)를 사용하는지 궁금합니다.

예. 그러나 건전한 이유 (IMO)는 거의 없습니다.

사람들은 사용해야 ArrayList할 때 사용했기 때문에 화상을 입습니다 List.

  • 유틸리티 방법은 좋아하는 Collections.singletonList(...)또는 Arrays.asList(...)을 반환하지 않습니다 ArrayList.

  • ListAPI의 메소드 는 동일한 유형의 목록을 리턴한다고 보장하지 않습니다.

사람의 예를 들어에 태워지고 https://stackoverflow.com/a/1481123/139985 포스터했기 때문에 "슬라이스"문제 ArrayList.sublist(...)를 반환하지 않습니다 ArrayList... 그리고 그가 사용에 자신의 코드를 설계 한 ArrayList유형으로 그의 모든 목록 변수. 그는 하위 목록을 new에 복사하여 문제를 "해결"했습니다 ArrayList.

List동작 에 대해 알아야 할 주장 은 주로 RandomAccess마커 인터페이스 를 사용하여 해결됩니다 . 그렇습니다, 그것은 약간 어색하지만 대안은 더 나쁩니다.

또한 상황에 실제로 (1) 이상 (2) (즉, (2)로는 충분하지 않습니다.

질문의 "빈도"부분은 객관적으로 대답 할 수 없습니다.

(그리고 예를 들어 주시겠습니까?)

경우에 따라 응용 프로그램은 메소드 사용하는 것이 필요할 수 ArrayList있습니다 API 하지 에서 ListAPI를. 예를 들어 ensureCapacity(int), trimToSize()또는 removeRange(int, int). 마지막 메소드는 메소드를로 선언하는 ArrayList의 하위 유형을 작성한 경우에만 발생합니다 public.

이것이 인터페이스 IMO가 아닌 클래스로 코딩하는 유일한 이유입니다.

(일부 플랫폼에서 ... 일부 플랫폼에서 ... 일부 성능에서 약간의 성능 향상을 얻을 수 있지만 이론상으로 마지막 0.05 %가 실제로 필요한 경우가 아니면이 작업을 수행 할 가치가 없습니다. 건전한 이유, IMO.)


임의 액세스의 효율성 여부를 모르면 효율적인 코드를 작성할 수 없습니다.

그것은 유효한 포인트입니다. 그러나 Java는이를 처리하는 더 나은 방법을 제공합니다. 예 :

public <T extends List & RandomAccess> void test(T list) {
    // do stuff
}

구현하지 않은 목록으로 호출 RandomAccess하면 컴파일 오류가 발생합니다.

instanceof정적 타이핑이 너무 어색한 경우 ...를 사용하여 동적으로 테스트 할 수도 있습니다 . 또한 목록이 임의 액세스를 지원하는지 여부에 따라 다른 알고리즘을 동적으로 사용하도록 코드를 작성할 수도 있습니다.

참고 ArrayList구현하는 유일한 목록 클래스되지 않습니다 RandomAccess. 기타 포함 CopyOnWriteList, StackVector.

나는 사람들이 그것에 대해 같은 주장을하는 것을 보았습니다 Serializable( List구현하지 않기 때문에) ... 그러나 위의 접근법은이 문제도 해결합니다. ( 런타임 유형을 사용하여 해결할 수있는 정도까지 . ArrayList직렬화 할 수없는 요소가 있으면 직렬화에 실패합니다.)


예를 들어 LinkedList응용 프로그램에 가장 적합한 선택을 결정할 ArrayList있지만 나중에 성능상의 이유로 더 나은 선택이 될 수 있습니다.

사용하다:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

대신에:

ArrayList list = new ArrayList();

참고로 :

여기에 이미지 설명을 입력하십시오

(주로 컬렉션 다이어그램에 게시)


되어 좋은 스타일로 간주 에 대한 참조를 저장 HashSet또는 TreeSetSet 타입의 변수에.

Set<String> names = new HashSet<String>();

이 방법을 TreeSet대신 사용하기로 결정한 경우 한 줄만 변경하면 됩니다.

또한 세트에서 작동하는 메소드는 Set 유형의 매개 변수를 지정해야합니다.

public static void print(Set<String> s)

그런 다음 이 메소드를 모든 구현에 사용할 수 있습니다 .

이론적으로, 링크 된리스트에 대해 동일한 권장 사항을 작성해야합니다. 즉, LinkedList 참조를 List 유형의 변수에 저장해야합니다. 그러나 Java 라이브러리에서 List 인터페이스는 클래스 ArrayListLinkedList클래스에 공통 입니다. 특히,이 방법은 링크 된 목록에 대해 비효율적이지만 랜덤 액세스를위한 get 및 set 메소드를 가지고 있습니다.

당신은 효율적인 코드를 작성할 수 없습니다 당신이 랜덤 액세스가 효율적인지 여부를 모르는 경우.

이것은 표준 라이브러리에서 심각한 디자인 오류이므로 이러한 이유로 List 인터페이스를 사용하는 것이 좋습니다.

그 오류가 얼마나 난처한 지 보려면 Collections 클래스 binarySearch메소드에 대한 소스 코드를 살펴보십시오 . 이 메서드는 List 매개 변수를 사용하지만 이진 검색은 연결된 목록에 적합하지 않습니다. 코드는 서투르게 목록이 링크 된 목록인지 확인한 다음 선형 검색으로 전환합니다!

Set인터페이스와 Map인터페이스가 잘 설계되고, 당신이 그들을 사용해야합니다.


코드가 목록의 "소유자"인 경우 (2)를 사용합니다. 예를 들어 로컬 전용 변수의 경우에 해당됩니다. List대신 추상 유형을 사용할 이유가 없습니다 ArrayList. 소유권을 보여주는 또 다른 예 :

public class Test {

    // This object is the owner of strings, so use the concrete type.
    private final ArrayList<String> strings = new ArrayList<>();

    // This object uses the argument but doesn't own it, so use abstract type.
    public void addStrings(List<String> add) {
        strings.addAll(add);
    }

    // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
    public List<String> getStrings() {
        return Collections.unmodifiableList(strings);
    }

    // Here we create a new list and give ownership to the caller. Use concrete type.
    public ArrayList<String> getStringsCopy() {
        return new ArrayList<>(strings);
    }
}

쓰면 List실제로 객체가 List인터페이스 만 구현한다고 말하지만 객체가 속한 클래스는 지정하지 않습니다.

을 쓸 때 ArrayList객체 클래스가 크기 조정 가능 배열임을 지정합니다.

따라서 첫 번째 버전은 향후 코드를보다 유연하게 만듭니다.

Java 문서를보십시오.

클래스ArrayList -List인터페이스의 크기 조정 가능 배열 구현.

인터페이스List -순서 컬렉션 (순서라고도 함). 이 인터페이스의 사용자는 목록에서 각 요소가 삽입되는 위치를 정확하게 제어 할 수 있습니다.

Array -단일 유형의 고정 된 수의 값을 보유하는 컨테이너 객체.


(3) 컬렉션 myCollection = 새 ArrayList ();

나는 이것을 일반적으로 사용하고 있습니다. 그리고 List 메소드가 필요한 경우 에만 List를 사용합니다. ArrayList와 동일합니다. 항상 더 "좁은"인터페이스로 전환 할 수 있지만 더 "넓은"인터페이스로 전환 할 수는 없습니다.


나는 (2)를 사용하는 사람들이 Liskov 치환 원리 또는 의존성 역전 원리를 모른다고 생각합니다 . 아니면 정말 사용해야 ArrayList합니다.


실제로 (2)가 선호 될뿐만 아니라 필수 인 경우가 있으며 여기서는 아무도 언급하지 않습니다.

직렬화!

직렬화 가능 클래스가 있고 목록을 포함 ArrayList하려면 List인터페이스가 확장되지 않기 때문에 필드를 구체적이고 직렬화 가능 유형으로 선언해야합니다.java.io.Serializable

분명히 대부분의 사람들은 직렬화가 필요 없으며 이것을 잊어 버립니다.

예를 들면 :

public class ExampleData implements java.io.Serializable {

// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();

다음 두 가지 중

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

우선은 일반적으로 선호됩니다. List인터페이스에서만 메소드를 사용할 것이기 때문에 향후 구현과 List같은 다른 구현을 자유롭게 사용할 수 LinkedList있습니다. 따라서 특정 구현에서 분리됩니다. 이제 두 가지 언급 할 가치가 있습니다.

  1. 우리는 항상 인터페이스를 프로그래밍해야합니다. 여기.
  2. 거의 항상 ArrayList이상 을 사용 하게 LinkedList됩니다. 여기.

누군가 (2)를 사용하는지 궁금합니다.

예 (때로는 읽지 않습니다). ArrayList인터페이스의 일부는 아니지만 구현의 일부인 메소드가 필요할 때 List. 예를 들어 ensureCapacity.

또한 상황에 실제로 (1) 이상 (2)

거의 항상 옵션 (1)을 선호합니다. 이것은 특정 구현 및 프로그램에서 인터페이스로 코드를 항상 분리하려고하는 OOP의 고전적인 디자인 패턴입니다.


List는 인터페이스이며 메서드가 없습니다. List 참조에서 메소드를 호출 할 때 실제로 두 경우 모두 ArrayList의 메서드를 호출합니다.

그리고 미래를위한 당신은 변경할 수 있습니다 List obj = new ArrayList<>List obj = new LinkList<>또는 다른 형태의 구현의 목록 인터페이스를 제공합니다.


누군가이 문제를 다시 한 번 더 깊이 물어 보았습니다 (중복).

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");

    ArrayList<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");

}

바이트 코드 뷰어를 사용하는 경우 ( http://asm.ow2.org/eclipse/index.html 사용 ) 목록 스 니펫에 대해 다음 (목록 초기화 및 할당 만)을 보았습니다 .

   L0
    LINENUMBER 9 L0
    NEW ArrayList
    DUP
    INVOKESPECIAL ArrayList.<init> () : void
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 1: list
    LDC "a"
    INVOKEINTERFACE List.add (Object) : boolean
    POP
   L2
    LINENUMBER 11 L2
    ALOAD 1: list
    LDC "b"
    INVOKEINTERFACE List.add (Object) : boolean
    POP

그리고 명부를 위해 :

   L3
    LINENUMBER 13 L3
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 2
   L4
    LINENUMBER 14 L4
    ALOAD 2
    LDC "a"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L5
    LINENUMBER 15 L5
    ALOAD 2
    LDC "b"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP

차이점은 목록 호출 끝 INVOKEINTERFACE을 반면 aList가 호출 INVOKEVIRTUAL을 . Bycode Outline Plugin 참조에 따라

invokeinterface는 Java 인터페이스 내에 선언 된 메소드를 호출하는 데 사용됩니다.

가상 호출

인터페이스 메소드 (invokeinterface를 사용), 정적 메소드 (invokestatic을 사용) 및 invokespecial이 처리하는 특수한 경우를 제외한 모든 메소드를 호출합니다.

요약하면, invokevirtual 은 invokeinterface 동안 스택에서 objectref 를 팝합니다.

인터프리터는 피연산자 스택에서 'n'항목을 팝합니다. 여기서 'n'은 바이트 코드에서 가져온 8 비트 부호없는 정수 매개 변수입니다. 이러한 항목 중 첫 번째 항목은 objectref이며, 메소드가 호출되는 오브젝트에 대한 참조입니다.

이것을 올바르게 이해하면 차이점은 기본적으로 각 방법이 objectref를 검색하는 방법 입니다.


GWT를 사용할 때 (2) 더 나은 곳을 알 수있는 유일한 경우는 응용 프로그램 설치 공간을 줄이므로 내 아이디어는 아니지만 Google 웹 툴킷 팀은 말합니다. 그러나 JVM (1) 내에서 정기적으로 실행되는 Java의 경우 항상 더 좋습니다.


나는 1이 선호된다고 말하지 않을 것이다.

  • ArrayList에서 선택적 동작 *의 구현에 의존하고 있습니다.이 경우 명시 적으로 ArrayList를 사용하는 것이 더 명확합니다.
  • 선택적 동작 또는 성능 특성을 위해 ArrayList가 필요한 메소드 호출에서 ArrayList를 사용합니다.

내 생각에 99 %의 경우 List로 얻을 수 있습니다.

  • 예를 들어 removeAll, 또는add(null)

List인터페이스는 여러 가지 수업을 - ArrayListLinkedList. LinkedList인덱싱 된 컬렉션 ArrayList을 만들고 정렬 된 목록을 만드는 데 사용됩니다 . 따라서 당신은 당신의 주장에 그것을 사용할 수 있지만, 코드, 라이브러리 등을 사용하는 다른 개발자가 당신이 사용하는 것뿐만 아니라 다른 유형의 목록을 사용하도록 허용 할 수 있습니다.

ArrayList<Object> myMethod (ArrayList<Object> input) {
   // body
}

당신은 단지 그것을 사용할 수 ArrayList없습니다, LinkedList하지만 당신의 사용을 허용 할 수 List있는 인터페이스를 사용하여이를 허용 할 수 있도록 방법을 사용하고 다른 장소에 클래스를, 그것은 단지 당신의 choise입니다 :

List<Object> myMethod (List<Object> input) {
   // body
}

이 메소드 인수 List에서 사용하려는 클래스를 사용할 수 있습니다.

List<Object> list = new ArrayList<Object> ();

list.add ("string");

myMethod (list);

결론:

가능하면 언제 어디서나 인터페이스를 사용하십시오. 본인이나 다른 사람이 사용하려는 다른 방법을 사용하도록 제한하지 마십시오.

참고 URL : https://stackoverflow.com/questions/2279030/type-list-vs-type-arraylist-in-java



반응형