국비교육(22-23)

18일차(3)/java(22) : Util Class (ArrayList, HashMap)

서리/Seori 2022. 10. 31. 22:21

18일차(3)/java(22) : Util Class (ArrayList, HashMap)

 

Step12_UtilClass 프로젝트 생성

 

 

<MainClass01>

package test.main;

import java.util.ArrayList;

/*
 * java에서 배열은 크기를 조절할 수 없는 고정 배열이다.
 * 따라서 동적으로 item을 추가하고 삭제하고 하는 작업을 하려면 일반배열은 사용할 수 없다.
 * 그래서 해당 작업을 하려면 ArrayList 객체를 사용하면 된다.
 */
public class MainClass01 {
	public static void main(String[] args) {
		//String type을 저장할 수 있는 ArrayList 객체생성
		ArrayList<String> names=new ArrayList<>();
		//"바나나", "딸기", "복숭아" 3개의 String type을 저장해 보세요.
		names.add("바나나");
		names.add("딸기");
		names.add("복숭아");
		//0번방의 아이템을 불러와서 item이라는 변수에 담아보세요.
		String item=names.get(0);
		//1번방의 아이템을 삭제하려면?
		names.remove(1);
		//0번방에 "아이스크림"을 넣고 싶으면?
		names.add(0, "아이스크림");
		//저장된 아이템의 갯수(size)를 size라는 지역변수에 담아 보세요.
		int size=names.size();
		//저장된 모든 아이템 전체 삭제
		names.clear();
	}
}

- ArrayList 배열식 사용방법 

 

- ArrayList는 java.Util이라는 패키지 안에 들어있다.

 

- ArrayList 배열인데, 이 배열에 어떤 타입을 저장할 것인지 정하는 것이다.

  ArrayList<데이터타입> 배열을 담을 변수명

- javascript 의 let names=[ ]; 와 비슷하다.

 

 


- 점을 찍어보면 여러 메소드들이 있다. 이 중 아이템을 추가할 때는 add 사용.(javascript의 push)

- ArrayList에 String type을 담을 예정이라서 <>을 String으로 넣어줌

- 위의 add는 String 값을 받는 메소드이다.

- 밑의 add는 인덱스번호를 넣으면 원하는 인덱스에 넣을 수 있는 메소드이다.

 → add 메소드가 다중정의되어 있다(overloading) 고 한다.

 

names.get(0);

- 위와 같은 형태로 인덱스번호를 받아 값을 가져온다. javascript array 였다면 names[0] 이라고 썼을 것.

 

- remove도 다중정의되어 있다.

 삭제할 아이템의 값을 알려주는가(int) / 참조값을 알려주는가(object) 의 차이!

 

 

참고) 메소드의 이름들은 대체로 비슷한데, 보통 직관적으로 알 수 있게 되어있다.
.addXXX : 새로운 것을 더하는 메소드 
.setXXX : 구성을 갖추고 세팅하는 메소드 
.getXXX : 아이템을 참조하고 가져오는 메소드 

 

names = [바나나, 딸기, 복숭아]

- 디버그모드를 보면 names.add를 통해 배열이 생성된 것을 볼 수 있다.

 

- 1번을 삭제하면 뒤에 있는 2번 인덱스가 앞으로 당겨지고,

  0번 방에 값을 새로 넣으면 인덱스가 다 하나씩 뒤로 밀린다.

 


 

- ArrayList 설명

- 상속 클래스 타입으로 4개, 인터페이스 타입으로 6개 : 총 10가지 타입으로 받을 수 있다.

- ArrayList, List 타입을 가장 많이 쓴다.

- 자주 사용되는 add/remove/get/clear/size 는 List 인터페이스에 정의되어 있다.

  List 인터페이스를 구현한 객체들은 모두 저 메소드(빨간 박스)가 동일하다.

 

ArrayList<String> names=new ArrayList<>();

List<String> names=new ArrayList<>();

- 위를 아래와 같이 바꿔도 오류가 없다.

 


 

<MainClass02>

package test.main;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class MainClass02 {
	public static void main(String[] args) {
		//ArrayList 객체를 생성해서 List 인터페이스 type 지역변수에 참조값 담기
		List<String> names=new ArrayList<>();
		names.add("바나나");
		names.add("딸기");
		names.add("복숭아");
		names.add("사과");
		names.add("오렌지");
		//반복문 돌면서 친구 이름을 순서대로 콘솔창에 출력해보기	
		for(int i=0; i<names.size(); i++) {
			String tmp=names.get(i);
			System.out.println(tmp);
		}
		System.out.println("----확장 for문을 사용하면 ----");
		for(String tmp:names) {
			System.out.println(tmp);
		}
		
		System.out.println("----forEach() 메소드 활용1 ----");
		Consumer<String> con=new Consumer<>() {
			@Override
			public void accept(String t) {
				System.out.println(t);
			}			
		};			
		names.forEach(con);
		
		System.out.println("----forEach() 메소드 활용2 ----");
		Consumer<String> con2=(t)->{
			System.out.println(t);			
		};			
		names.forEach(con2);
		
		System.out.println("----forEach() 메소드 활용3 ----");
		names.forEach((t)->{
			System.out.println(t);
		});
	}
}

- ArrayList에서 for 문 작성하기

 

 

1) 일반 for문

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

 

 

2) 확장 for문

for(String tmp:names) {
	System.out.println(tmp);
}

- 배열을 담을 임시변수를 콜론 : 좌측에 써주면 알아서 for문이 돌면서 tmp배열을 하나씩 출력해준다.

 

 

3) forEach 함수 사용

 

- javascript: forEach에 함수를 넣어주면 배열의 아이템만큼 호출되면서 item에 순서대로 값이 전달된다.

- java: forEach에 Consumer type 참조값을 넣어준다.(함수가 단독으로 존재할 수 없으므로)


- Consumer type의 참조값을 넣어주면 인터페이스 안에 있는 메소드(1개짜리 메소드)가

  배열의 아이템만큼 알아서 호출되면서 배열의 아이템을 하나씩 내보내준다.

 

 

Consumer<String> con=new Consumer<>() {
    @Override
    public void accept(String t) {
        System.out.println(t);
    }			
};			
names.forEach(con);

- Consumer 인터페이스를 구현하는 익명 클래스 사용
Consumer<String> con=new Consumer<>() { };   ← class ? implements Consumer

- Consumer가 하나씩 호출하면서 accept라는 메소드에 값이 순서대로 하나씩 전달된다.

 

 

4) forEach 함수 활용 2

Consumer<String> con2=(t)->{
    System.out.println(t);			
};			
names.forEach(con2);

- override, 데이터 타입을 생략하고 참조값을 con2 변수에 넣어준다.

 

 

5) forEach 함수 활용 3

names.forEach((t)->{
    System.out.println(t);
});

- 배열명.forEach(()->{}); 형태로 만들어 줄 수도 있다.(메소드가 하나만 있는 경우)

- 굳이 변수를 만들 필요가 없으므로 con2라는 변수마저도 생략한 것!

 


 

<MainClass03>

package test.main;

import java.util.ArrayList;
import java.util.List;

public class MainClass03 {
	public static void main(String[] args) {
		//정수를 저장할 수 있는 ArrayList 객체를 생성해서 참조값을 List 인터페이스 type의 지역변수 nums에 담기
		List<Integer> nums=new ArrayList<Integer>();
		nums.add(10);
		nums.add(20);
		nums.add(30);
		
		for(Integer tmp:nums) {
			System.out.println(tmp);			
		}
		System.out.println("-----");
		nums.forEach((item)->{
			System.out.println(item);
		});
	}
}

 

List<Integer> nums=new ArrayList<Integer>();

 

- ArrayList 타입은 기본 데이터 타입(8종)은 담지 못한다.
- List<int>, List<double>, List<float>, ... 등등은 존재하지 않는다.
- Generic이 클래스가 아니기 때문에 담을 수 없다.

- 그렇다면? 객체에 포장을 해서 담아야 한다.(WrapperClass)
 기본 데이터타입의 객체형을 사용해야 한다.

 

- List에 들어갈 수 있는 타입이 Integer 타입으로 wrapping 되었다.

 알아서 객체형으로 포장되므로 그냥 쓰면 된다!

 

 

for(Integer tmp:nums) {
	System.out.println(tmp);
}

- 확장 for문으로 작성한다. for문 안에는 int를 써도 되고 Interger를 써도 된다.

 

- 디버그모드에서 보면 배열에 그냥 값이 들어가는 것이 아니라 integer 객체에 포장되어 들어가 있다.

(참조값id 가짐)

 

nums.forEach((item)->{
    System.out.println(item);
});

- forEach 를 사용하면 이렇게 쓸 수 있다. item 을 바로 함수에 넣어준다고 생각하기.

 


 

<Car> 클래스 생성

package test.mypac;

public class Car {
	//필드
	private String name;
	//생성자
	public Car(String name) {
		this.name=name;
	}
	//메소드
	public void drive() {
		System.out.println(this.name+" 이(가) 달려요~!");
	}
}

 

<MainClass04>

package test.main;

import java.util.ArrayList;
import java.util.List;

import test.mypac.Car;

public class MainClass04 {
	public static void main(String[] args) {
		//1. Car type을 저장할 수 있는 ArrayList 객체를 생성해서
		//참조값을 List 인터페이스 type 지역변수 cars에 담아 보세요.		
		List<Car> cars=new ArrayList<>();
		//2. Car 객체(3개)를 생성해서 List 객체에 저장해 보세요.
		cars.add(new Car("소나타"));
		cars.add(new Car("프라이드"));
		cars.add(new Car("아반떼"));
		//3. 반복문 for문을 이용해서 List 객체에 저장된 모든 Car 객체의 drive() 메소드를 순서대로 호출해보세요.		
		for(Car tmp:cars) {
			tmp.drive();
		}
		System.out.println("--- 기본 for문 ---");
		for(int i=0; i<cars.size(); i++) {
			cars.get(i).drive();
		}		
		System.out.println("--- Consumer 인터페이스를 활용하면 ---");
		cars.forEach((item)->{
			item.drive();
		});
	}
}

 

- Car 클래스. 생성자에서 name을 만들어서 → 필드에 전달 메소드에 전달

List<Car> cars=new ArrayList<>();
- List에 car 객체를 담겠다는 의미미으로 generic< > 에 car를 넣어주기
- new Car 한 객체(의 참조값)를 ArrayList에 하나씩 저장!

Car e=new Car();
- 코딩이 잘 안 되면 대입연산자를 떠올리기.

 

- 그런데 저 경우에는 에러가 난다.

Car에 기본 생성자가 없고, String 타입 매개변수를 필요로 하는 생성자만 있기 때문이다.

cars.add(new Car("소나타"));
- 이렇게 생성자에 string 타입을 전달해 주어야만 생성할 수 있다.

 

for(Car tmp:cars) {
	tmp.drive();
}

- 확장 for문 작성하기. 임시변수 tmp


- new를 3번 해서 각각의 car 객체의 필드에 저장된 값이 다르기 때문에

 똑같은 메소드를 사용해도( drive(); ) 다른 값이 나온다.

 


 

<Member> 클래스 생성

package test.mypac;

public class Member {
	public int num;
	public String name;
	public String addr;
	
	/*
	 * 기본 생성자도 필요하다면 정의할 수 있다.
	 * 생성자는 다중정의가 가능하다.
	 * 따라서 어떤 객체를 생성하는 방법이 여러가지가 될 수도 있다는 것이다.
	 */	
	public Member() {}

	//필드에 저장할 값을 전달받는 생성자
	public Member(int num, String name, String addr) {
		this.num=num;
		this.name=name;
		this.addr=addr;
	}
}

- 필드 3개, 2종류의 생성자가 있는 클래스 생성

 

<MainClass05>

package test.main;

import java.util.ArrayList;
import java.util.List;

import test.mypac.Member;

public class MainClass05 {
	public static void main(String[] args) {
		//1. Member 객체를 담을 수 있는 ArrayList 객체를 생성해서 참조값을 members 라는 지역변수에 담아 보세요.
		List<Member> members=new ArrayList<>();
		//2. 3명의 회원정보를 Member객체에 담아 보세요.(Member객체가 3개 생성되어야 함)
		Member m1=new Member();
		m1.num=1;
		m1.name="바나나";
		m1.addr="서울";
		Member m2=new Member(2,"딸기","부산");
		Member m3=new Member(3,"복숭아","대전");		
		//3. 위에서 생성된 Member 객체의 참조값을 members List 객체에 모두 담아 보세요.
		members.add(m1);
		members.add(m2);
		members.add(m3);
		/*
		 * 4. members List 객체에 담긴 내용을 이용해서 회원 목록을 아래와 같은 형식으로
		 *    반복문 돌면서 출력해 보세요.
		 * 
		 * 번호: 1, 이름: 바나나, 주소: 서울
		 * 번호: 2, 이름: 딸기, 주소: 부산
		 * 
		 */
		for(Member tmp:members) {
			System.out.println("번호: "+tmp.num+", 이름: "+tmp.name+", 주소: "+tmp.addr);
		}
		System.out.println("---기본 for문---");
		for(int i=0; i<members.size(); i++) {
			System.out.println("번호: "+members.get(i).num+", 이름: "+members.get(i).name+", 주소: "+members.get(i).addr);
		}
	}
}

- 클래스명은 대문자, 필드명/메소드명/매개변수는 소문자로 시작하는 것이 일반적이다.

 

 

Member m1=new Member();
m1.num=1;
m1.name="바나나";
m1.addr="서울";
- 디폴트 생성자를 사용해 객체를 생성하면 0, null, null로 나오므로 하나씩 넣어주어야 한다.

(필드에 직접 접근하는 것은 대부분 막혀있지만, public으로 되어 있는 필드라면 직접 값을 넣을 수 있다.)

Member m1=new Member(1, "바나나", "서울");
- 대신 이렇게 작성할수도있다. (객체를 생성하면서 바로 필드 값 전달)

 

- add에는 member타입의 값을 넣어주어야 한다.

- members.add(); 가 종료된 후 arrayList 타입 members 배열 안에 각각 m1,m2,m3에서 참조한 값이 들어갔다.

 (members[0] 의 값과 m1의 참조값이 id=40으로 동일한 것 참고!)

 

- for문 안에서 tmp가 members를 하나씩 순서대로 참조하고 있다.

 

- ArrayList 객체는 어떤 데이터를 순서 있게 관리하고 싶을 때 사용한다.
 즉, 순서가 중요한 데이터를 저장하고자 할 때 사용한다.

 


 

<MainClass06> : HashMap

package test.main;

import java.util.HashMap;
/*
 * java에서 어떤 데이터를 key:value의 쌍으로 관리하고 싶으면 HashMap객체를 사용한다.
 * key의 generic도 마음대로 지정할수있지만 보통 String type으로 한다.
 * value의 generic은 그때그때 다르지만 value가 여러가지 type이면 Object로 지정한다.
 */
public class MainClass06 {
	public static void main(String[] args) {
		/*
		 * 번호:1, 이름:바나나, 남자여부:false (숫자,문자,불리언)
		 */
		HashMap<String, Object> map1=new HashMap<>();
		map1.put("num", 1);
		map1.put("name", "바나나");
		map1.put("isMan", false);
		
		int num=(int)map1.get("num");
		String name=(String)map1.get("name");
		boolean isMan=(boolean)map1.get("isMan");
		
		//동일한 key 값으로 다시 담으면서 수정
		map1.put("name", "무화과");
		//특정 key 값으로 담긴 내용 삭제, 성공하면 true 실패하면 false 리턴
		map1.remove("isMan");
		//모두 삭제
		map1.clear();
	}
}

 

 

- java의 HashMap 객체를 사용하면 javascript의 object와 같이 key:value 형태로 데이터를 관리할 수 있다.

- 순서가 없는 데이터의 관리에 적합하다.

 

- 생성자는 3가지 종류가 있는데, 기본을 제일 많이 쓴다.(아무 전달값 없이)

 

- 입력하면 <K, V>가 자동완성된다. HashMap도 generic class가 있다.

- java에서 어떤 데이터를 key:value의 쌍으로 관리하고 싶으면 HashMap객체를 사용한다.

- java에서는 키 값도 마음대로 지정할 수 있다.
- key의 generic도 마음대로 지정할 수 있지만 보통 String type으로 한다.

- value의 generic은 그때그때 다르지만 value가 여러가지 type이면 보통 Object로 지정한다.

 → 이유는 Object 타입으로 하면 모든 타입의 값을 넣을 수 있으므로!

 

.put : 각각의 key, value 값을 대입하는 메소드 (값 넣어주기)

 

.containsValue : 안에 그값이 들어있는지 확인해서 불리언을 리턴하는 메소드
.getOrDefault : 특정 키값으로 저장된 값을 가지고오는 메소드
.putIfAbsent : 만약 그 값이 없다면 넣는 메소드

 

- String key="num", "name", "isMan"   → String type 만 가능

- Object value=1, "바나나", false   → 모든 타입이 전부 들어갈 수 있다!

  (사실 값이 그대로 들어가는 건 아니고 참조 데이터타입으로 포장boxing되어서 들어가는 것)

 

.get : key값을 넣어주면 object 타입으로 리턴한다.(저장되어 있는 값 참조하기)

 

int num=(int)map1.get("num");
String name=(String)map1.get("name");
boolean isMan=(boolean)map1.get("isMan");

- 하지만 object 타입이 필요한 것이 아니므로 int 타입으로 받아준다.

→ object타입은 int에 담길 수 없다. 따라서 casting 과정 필요! (더 좁은 범위의 타입으로 바꾸어준다)

 (object 타입을 사용하면 저장할 때는 편하지만, 가지고 올 때 캐스팅의 번거로움이 있다.)

 


 

ArrayList → .add() .get() .size()
HashMap → .put() .get()

 

- 이 정도를 제일 많이 쓴다. 사실 수정하거나 삭제할 일은 거의 없다.
- 저장 / 참조하는 경우가 대부분. 대부분의 데이터를 read only(읽기 전용)로 사용한다.

 

- HashMap도 클래스 설명에 들어가보면 타입으로 받을 수 있는 부모클래스와 인터페이스가 많다.(hashmap 클래스 설명)

- ArrayList - List 의 관계와 마찬가지로 HashMap - Map, Map 인터페이스 타입으로 받아서 많이 쓴다.
  (put, get, clear 등도 Map 인터페이스 안에 들어있는 메소드이다)

- HashMap의 key는 거의 String 타입 고정이라고 생각하면 된다.