Backend/Java

[Java] Collection_ List

Deeb 2021. 10. 6. 01:07

✍Collection (컬렉션 )

:  메모리상에서 자료를 구조적으로 처리하는 방법을 자료구조라 일컫는데
컬렉션(Collection)은 자바에서 제공하는 자료구조를 담당하는 프레임워크 == 자료구조들의 모음

java.util 패키지에 포함되며, 인터페이스를 통해 정형화된 방법으로 다양한 컬렉션 클래스 이용 가능

 

배열의 문제점 & 컬렉션의 장점


배열의 문제점 컬렉션의 장점(특징)
번 크기를 지정하면 변경할 수 없다. 저장하는 크기의 제약이 없다.
배열에 기록된 데이터에 대한 중간 위치의 추가, 삭제가 불편하다. 추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결된다
- 자료를 구조적으로 처리 하는 자료구조가 내장되어 있어 알고리즘 구현이 필요 없음
한 타입의 데이터만 저장 가능하다. 여러 타입의 데이터가 저장 가능하다
- 객체만 저장할 수 있기 때문에 필요에 따라 기본 자료형을 저장해야 하는 경우 Wrapper클래스 사용

1. 컬렉션의 주요 인터페이스

abstract method : 상속 받은 자식이 반드시 구현 ( 오버라이딩 강제)

-> 자식들이 모두 같은 메서드를 구현 (== 모두 동일한 이름의 기능이 있고 자식들이 비슷한 형태를 띄게 된다.)

 

List와 Set은 비슷한 점이 많다 == 추상화 특징을 가졌고 Map도 컬렉션에 포함된다.

 

똑같은 리스트여도 ArrayList와 LinkedList는 상황에 따라 맞는걸 쓰는거다

부모객체인 리스트를 참조하는 다형성의 특징을 보여준다.


🚩 List

자료들을 순차적으로 나열한 자료구조로 인덱스로 관리되며,
중복해서 객체 저장 가능 구현 클래스로 ArrayList와 Vector, LinkedList가 있음

 

자료들을 순차적으로 나열한 자료구조== 배열

 

 

List 계열 주요 메소드

➡ E (Element) :모든 요소 == 모든 객체

추가 - add / 수정 - set / 조회 - get / 삭제 - remove


1. ArrayList

- List의 후손 클래스
- 배열과 비슷한 형태의 리스트(가장 기본적이고 많이 사용된다.)

List의 후손으로 초기 저장 용량은 10으로 자동 설정되며 따로 지정도 가능
저장 용량을 초과한 객체들이 들어오면 자동으로 늘어나며 고정도 가능 (길이 +1) *2 한 크기로 늘어난다.
동기화(Synchronized)를 제공하지 않음
ArrayList list = new ArrayList(); // 기본 생성자로 생성 시 초기 용량 10		
ArrayList stdList = new ArrayList(3); // 초기 용량 3​

1) add(E e) 

리스트의 맨 끝에 추가( if문 가능)
// 초기용량 3인 stdList에 값 삽입
if (	stdList.add( new Student ("홍길동", 15, 2))	) {
System.out.println("학생 정보 추가 성공(ArrayList)");
}
if (	stdList.add( new Student ("고길동", 16, 3))	) {
System.out.println("학생 정보 추가 성공(ArrayList)");
}
if (	stdList.add( new Student ("희동이", 8, 1))	) {
System.out.println("학생 정보 추가 성공(ArrayList)");
}

//자동으로 ArrayList 길이가 증가하는지 확인 ==> 성공(자동으로 늘어나는 코드도 추가되어있다) 
if (	stdList.add( new Student ("김영이", 17, 1))	) {
System.out.println("학생 정보 추가 성공(ArrayList)");
}

 

2) toString()

리스트의 맨 끝에 추가 (if문 가능리스트에 저장된 모든 요소를 한 줄의 문자열로 반환)

 

System.out.println( stdList.toString() );
더보기

결과
[홍길동/15/2, 고길동/16/3, 희동이/8/1, 김영이/17/1]

 

3) add(int index, E e) 

지정된 index에 삽입 (if사용못함)

 

System.out.println( stdList.toString() );

stdList.add(1, new Student("박철수", 13, 6)); 
		
System.out.println("중간 삽입 확인");
System.out.println(stdList);
더보기

결과
[홍길동/15/2, 고길동/16/3, 희동이/8/1, 김영이/17/1]
중간 삽입 확인
[홍길동/15/2, 박철수/13/6, 고길동/16/3, 희동이/8/1, 김영이/17/1]

4) size() 

리스트에 저장된 데이터 개수 반환

 

System.out.println("저장된 학생 수 : " + stdList.size());
더보기

결과

저장된 학생 수 : 5

5) get(int index) : 리스트에 저장된 index번 째 요소 반환

리스트에 저장된 index번 째 요소 반환

 

System.out.println(stdList.get(0));
System.out.println(stdList.get(1));
System.out.println(stdList.get(2));
System.out.println(stdList.get(3));
System.out.println(stdList.get(4));

=> for문을 사용하기 아주 적합한 모양

더보기

결과  

저장된 학생 수 : 5
홍길동/15/2
박철수/13/6
고길동/16/3
희동이/8/1
김영이/17/1

5-1) for문, get(), size() 같이 이용하기

stdList의 길이만큼까지 반복하는데 인덱스i번째 값을 반복하는 for문

 

//length를 반환하지 않기때문에 size를 쓴다
    for(int i = 0; i<stdList.size() ; i++) {
    System.out.println(stdList.get(i)); 
    }

6) Object set(int index,E e)

리스트의 index 번째 요소를  e로 변경하고 기존에 있던 값을 반환

 

Object obj = stdList.set(0, new Student("이미니", 18, 2)); 
//stdList 0번째 인덱스에 있는값을 new Student로 바꾼다.

System.out.println("반환된 Object : " + obj);
System.err.println(stdList); //홍길동 대신 이미니가 들어가있다.

➡  set을 통해 변경되어진 값을 obj에 대입

더보기

결과
반환된 Object : 홍길동/15/2
[이미니/18/2, 박철수/13/6, 고길동/16/3, 희동이/8/1, 김영이/17/1]

 

7) Object remove(int index)

리스트의 index번째 요소를 삭제하고 기존에 있던 값을 반환

 

Object obj2 = stdList.remove(1);
		 System.out.println("제거된 학생 정보 : "+ obj2);//박철수 제거
		 System.out.println(stdList); //박철수 제거 확인
더보기

결과
제거된 학생 정보 : 박철수/13/6
[이미니/18/2, 고길동/16/3, 희동이/8/1, 김영이/17/1]

get에서 얻어오는 데이터의 자료형을 Object로 인식

자식안에있는 부모 모양만 참조한다 - 업캐스팅!!

 (Student)stdList.get(i)-- 다운 캐스팅

 

** add에 다른 자료형도 추가

// *다른 자료형 추가
		 stdList.add("문자열도 객체다");
		 
		 //컬렉션은 객체만 저장 가능하다. 
		 stdList.add(new Integer(100)); // 100 (int) -- Auto Boxing --> 100 (integer)
//		 integer라는 wrapper 클래스 (이제는 잘 안쓰는데 예제용)
		 
		 ArrayList list2 = new ArrayList();
		 list2.add( new Student("김갑수", 19, 3)	);
		 list2.add( new Student("김삼순", 19, 3)	);
		 
		 stdList.add(list2); //컬렉션은 객체도 가능하다 => 그래서 위에서 만든 list2도 가능
		 
		 // stdList에 저장된 정보 모두 출력
		 for(int i = 0; i <stdList.size(); i++) {
			 System.out.println(stdList.get(i));
		 }
더보기

//  결과
//  이미니/18/2
//  고길동/16/3
//  희동이/8/1
//  김영이/17/1 ---student
//  문자열도 객체다  --- string
//  100  ---integer
//  [김갑수/19/3, 김삼순/19/3] -- arraylist
 

➡ 문자열은 기본자료형이 아닌 참조형으로 컬렉션에서 사용 가능

 

** stdList의 요소 중 Student 객체에 접근하는 경우 이름만 출력하기

System.out.println("stdList의 요소 중 Student 객체에 접근하는 경우 이름만 출력하기"); // 데이터가 길어지면서 관리는 어렵다
    for(int i = 0; i<stdList.size(); i++) {

    //	stdList.get(i) --i번째 요소를 출력한다. -> 참조하고있는 인스턴스가 어떤 인스턴스인지 확인하는 instanceof사용
    if( stdList.get(i) instanceof Student) {
    //stdList의 i번째 요소가 Student 객체 또는 Student를 상속받은 객체인 경우

    //ArrayList에 저장되는 데이터,데이터 타입은 모두 Object 타입으로 인식된다. 
    //(자료형 관계없이 모두 들어오게하기위해 Object타입으로 인식)				
    //		stdList.get(i)를 통해서 얻어오는 데이터의 타입은 모두 Object

    System.out.println( ( (Student) stdList.get(i)	).getName() ); 
    //Object -> Student로 다운 캐스팅

    } else {

    System.out.println(stdList.get(i));
    }
    }
더보기

결과
//  stdList의 요소 중 Student 객체에 접근하는 경우 이름만 출력하기
//  이미니
//  고길동
//  희동이
//  김영이
//  문자열도 객체다
//  100
//  [김갑수/19/3, 김삼순/19/3]

 

8) 향상된 for문 (for each 문)

for( 하나씩 꺼내서 담을 변수 : 컬렉션 또는 배열명 )

- 컬렉션이나 배열의 모든 요소를 처음부터 끝까지 순서대로 접근하는 용도의 for문
굉장히 유용하고 꼭 알아둬야하고 컬렉션을 위해 존재
public void example3() {
  ArrayList list = new ArrayList();
  list.add("김밥");
  list.add("라면");
  list.add("탕수육");
  list.add("파스타");

  // 1) 기존 for문
  // 2) 향상된 for문
  }

  // ==> 위에 노란줄은 정보표시줄

// list에 저장된 데이터를 모두 출력

1) 기존for문

for(int i = 0; i < list.size(); i++) {
System.out.println(	list.get(i) );
}

2) 향상된 for문

for(	Object obj: list ) {
// for문이 반복될 때 마다 list에 저장된 요소를 처음부터 순서대로 하나씩 꺼내 obj변수에 저장 
System.out.println(obj);
}

9) Generics(제네릭, 제네릭스, 지네릭스) 

클래스, 메서드, 컬렉션 내부에 사용되는 클래스 타입을 한 종류로 제한하는 기능
-> 타입 제한

제네릭의 이점
1) 하나의 타입으로 제한이 되기 때문에 instanceof 같은 타입 검사, 다운캐스팅의 작업이 필요 없어진다.
    -> 코드 활용 방법이 쉬워짐
    == 컴파일 단계에서의 강력한 타입 체크 

2) 하나의 코드로 여러 타입 처리가 가능하다. (다형성)

 


⭐ 컬렉션 타입 제한 기능의 제네릭 사용

public void example4() {

// ** 컬렉션 타입 제한 기능의 제네릭 사용
	ArrayList<Student> stdList = new ArrayList<Student>(); 
	// Student로 타입이 제한된 ArrayList 객체 생성 
	// == 생성된 ArrayList에는 Student만 저장 가능
	// == 저장된 데이터는 모두 Student이다. 
		
	//add(Student e)
	stdList.add( new Student("김아무개", 9, 2));
	stdList.add( new Student("김스벅", 10, 3));
	stdList.add( new Student("김투썸", 11, 4));
	stdList.add( new Student("김할리스", 12, 5));
		
	//향상된 for문으로 모두의 이름 출력
	
    for(Student std	: stdList ) {
    // stdlist가 Student로 타입이 제한되어 있기 때문에
    // 하나씩 순서대로 꺼내서 저장하는 변수의 타입도 Student 타입을 사용한다.
    // Student 타입을 사용한다면 instanceof 검사가 필요 없다. 

    System.out.println( std.getName() );
    // std가 Student 타입이기 때문에 다운 캐스팅이 필요 없다. 
    }
}

 

--

리스트는 인터페이스의 성질을 갖고있어서 ArrayList, Vector, LinkedList가 List의 성질을 갖는다. 

예를들면 왼손, 오른손 둘 중 하나로 밥을 먹어도 밥을 먹는 행위는 된다. 

--

 

10)  컬렉션과 다형성 

  • 다형성이란? - 부모 타입의 참조변수로 상속 관계에 있는 자식 객체를 참조하는것
  • 인터페이스란? - 틀만 작성된 미완성의 설계도
  • 인터페이스의 특징 : 상속받은 자식에게 동일한 이름의 기능을 강제적으로 오버라이딩 시킨다 -> 자식들의 형태가 비슷하다.  +  객체 생성 x, 부모 타입 참조 변수 사용 O

ArrayList : 배열 모양의 List로 검색에 효율적이다. 
LinkedList : 요소 하나 하나가 줄로 연결된 모양으로 추가, 삭제에 효율적이다. 

 

🔎 List<String> 

list -컬렉션 <strig> -다형성 

 

1) 다형성 미적용 예제

public void example5() {	
//ArrayList<String> list1 = new ArrayList<String>();
  LinkedList<String> list1 = new LinkedList<String>();

  // 추가
  list1.add("AAA");
  list1.add("BBB");
  list1.add("CCC");
  list1.add("DDD");

  // 삭제
  list1.remove(0);
  list1.remove(0);
  list1.remove(0);

  //추가 삭제가 빈번하므로 LinkedList가 더 효율적임 
  --> 그래서 사용할 땐 위에 linkedlist로 하기
  
  ex5_1(list1); // 매개변수로 ArrayList<String> 전달
  list1 = ex5_2(); //반환값으로 ArrayList<String> 반환
}
//매개 변수가 ArrayList<String>인 메서드
//String으로 된 ArrayList를 받는다
 public void ex5_1(LinkedList<String> list) {
 System.out.println(list);
}
    
 //반환형이 ArrayList<String>인 메서드
 public LinkedList<String> ex5_2() {
 return new LinkedList<String>();
}
 // arraylist 에서 LinkedList로 변경하려면 총 5번 수정해야하는데 코드가 길어지면 이렇게 하는게 좋을까?

 

2) 컬렉션에 다형성 적용 예제

 

⭐ 컬렉션에 다형성 적용 시 이점
-> 유지 보수성 향상, 범용성 향상(어떤 list관련 코드에서든 사용가능)

 

public void exmaple6() {

  List<String> list2 = new LinkedList<String>();
// 부모 타입(list) 참조 변수로 자식 타입 객체(ArrayList) 참조
// -> LinkedList로 자식 객체를 변경해도 문제가 발생하지 않는다. 
// 이유 1. ArrayList, LinkedList 둘 다 List를 상속 받았기 때문에 사용하는 메서드의 이름이 같다. 
// 이유 2. ex6_1, ex6_2() 두 메서드의 매개변수, 반환형이 부모 타입으로 작성되어 있기 때문에 참조하는 자식 객체가 변해도 다형성이 항상 적용된다. 

  // 추가
  list2.add("AAA");
  list2.add("BBB");
  list2.add("CCC");
  list2.add("DDD");

  // 삭제
  list2.remove(0);
  list2.remove(0);
  list2.remove(0);

  //매개변수로 전달
  ex6_1(list2);
  //호출
  list2 = ex6_2();
//부모타입으로 쓰여서 누가 들어와도 가능하다. 
	}
	
	//매개 변수가 부모타입인 List<String> == 매개변수의 다형성 
	public void ex6_1( List<String> list) {
		System.out.println(list);
	
	}
	
	//반환형이 부모타입인 List<String> ==반환형의 다형성
	public List<String> ex6_2(){
		return new ArrayList<String>();
	}

 

3) Generic에 다형성 적용

하나의 코드로 여러 타입 처리가 가능하다. (다형성)는 Generic의 특징

public void example7() {
		
	List<Person> list = new ArrayList<Person>();
	// 제네릭이 부모타입인 Person으로 제한된 ArrayList객체를 생성
		
	list.add(new Person('남', 180.5)); // 부모타입 객체 추가
	list.add(new Student('여', 170, "주주주", 17, 1) ); // 자식 타입 객체 추가 -> 에러 없음 
		
	for(Person p : list) { //list안에 있는 값을 p에 담는다. 
		System.out.println(p.toString()); // 
	}						// 정적 바인딩
}
더보기

결과
남/180.5  -person의 값 //참조변수도 person 값도 person
주주주/17/1/여/170.0    -- student의 값이나옴,  

➡ person을 받는 p인데 왜 자료형인가?
참조변수는 부모인 person 참조당하는 객체는 자식객체 ==> 자식이 오버라이딩 했으니깐 자식쪽으로 이동

실행 중 오버라이딩 된 자식의 toString()가 연결됨 -> 동적 바인딩
동적 바인딩의 효과 : 하나의 코드로 여러 타입의 코드를 수행할 수 있다. 

 

 

2. Vector

- List의 후손
- ArrayList와 동등하지만 동기화(Synchronized)를 제공한다는 점이 ArrayList와 차이점 
List 객체들 중에서 가장 성능이 좋지 않음

 

3. LinkedList

- List의 후손
- 인접 참조를 링크해 체인처럼 관리 특정 인덱스에서 객체를 제거하거나 추가하게 되면 바로 앞/뒤 링크만 변경하면 되기 때문에
객체 삭제와 삽입이 빈번하게 일어나는 곳에서는 ArrayList보다 성능이 좋음

 

4. Comparable, Comparator

Collections.sort()

Collections.sort(List list)  T객체에 Comparable을 상속받아 compareTo 메소드 재정의를 통해 정렬 구현 (단 한 개의 정렬) Collections.sort(List list, Comparator c)  지정한 Comparator클래스에 의한 정렬 (여러 개의 정렬)
반응형