본문 바로가기
공부/java

15일차 복습

by 샤샤샤샤 2022. 12. 4.

콜렉션 클래스

리스트, 맵, 셋을 포함하는 상위 클래스.

 

Hash란?

초보자인 만큼, 깊게 파지 않고 쉽게 설명하자.

간단하게 말해 Arrray List의 문제를 해결하기 위해서 나온 방식이다. 크게 map과 set이 존재한다.

ArrayList는 검색이 빠를지 모르나 아이템의 추가/삭제가 힘들다. 그러나 map은 인덱스를 특정한 값과 연결시켜 저장하기에 (Key와 Value) 필요한 데이터만 호출하는데 편리하다. 또한 set는 중복을 허용하지 않고 순서가 없다는 특징, 즉 인덱스가 부여되지 않는다는 점 덕분에 내부에 어떤 값이 존재하는지 빠르게 확인할 수 있다.

 

HashMap

Key와 Value값으로 저장된다.

여기서 Key란, 인덱스 값에 또다른 값을 저장한 것이다.

Map<String, String> map = new HashMap<>();
map.put("name", "james");

예를 들어서 위의 경우를 보자. name이라는 Key와 james라는 Value를 가진 해쉬맵이다.

리스트나 배열이라면 인덱스 1 값에 james를 저장하겠지만, Map구조에서는 name을 호출하면 리스트 1이 호출되고, 이어서 거기에 저장된 james라는 값이 다시 호출되는 형식이다.

복잡해보이지만 이런 형식으로 데이터를 저장하면 특정 값이 무엇을 의미하는 알기 편해져, 모든 배열을 일일이 확인하지 않고도 필요한 값만 호출할 수 있다.

이런 특징 덕분에 자바스크립트의 기본 표준 포멧이 되었으며, 네트워크를 통해 자료를 주고 받을때 주로 사용하는 구조다.

 

위의 map객체는 <String, String> 라는 제네릭이 첨언되어 있는데, 이는 Key 와 Value모두 String 구조라는 뜻이다.

만약 위의 코드를 출력하면 어떻게 나올까?

import java.util.HashMap;

public class ex43 {
    public static void main(String[] args) {
        HashMap<String,String> sample = new HashMap<>();
        sample.put("name","james");
        System.out.println(sample);
            }
        }       출력값 : {name=james}

리스트는 [ ](대괄호)로 감싸지만, map는 { }(중괄호)로 감싼다.

만약 리스트의 Value값만 받고 싶다면 get()함수를 사용해야 한다.

System.out.println(sample.get("name"));
출력값 : james

Value값만 따로 받을때는 중괄호가 없다.

 

map은 인덱스가 없기에 for문을 통한 순환이 불가능하다. 따라서 향상된 for문, 또는 이터레이터를 사용해야 한다.

Iterator의 사용법은 다음과 같다.

1. map자료 안의 key들을 모두 얻을수 있는 keySet(); 함수를 이용해 모든 Key를 Set로 받기.

1.  Iterator 클래스 객체를 1에서 얻은 Key 값들로 성생.

2. whil문 조건으로 hasNext()함수 넣기.

4. 새로운 String 형식 변수에 next()함수를 이용해 얻은 값을 넣기.

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class ex43 {
    public static void main(String[] args) {
        HashMap<String,String> sample = new HashMap<>();
        sample.put("name","james");
        sample.put("face","good");
        sample.put("age","23");
        Set<String> keys = sample.keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()){
            String key = it.next();
            System.out.println( sample.get(key) );
        }
    }
}  결과 :  good
           james
           23

다소 복잡해 보이지만, Set 형식으로 key값들을 불러온다는 것을 제외하면 리스트에서 사용할때와 똑같은 규칙이 적용된다.


이제 향상된 for문을 보자.

import java.util.HashMap;
import java.util.Set;

public class ex43 {
    public static void main(String[] args) {
        HashMap<String,String> sample = new HashMap<>();
        sample.put("name","james");
        sample.put("face","good");
        sample.put("age","23");
        Set<String> keys = sample.keySet();
        for(String key : keys){
            System.out.println(sample.get(key));
        }
    }
}
결과 : good
       james
       23

이것 역시도 Set 형식으로 keySet() 함수를 사용한다는 것을 제외하면 기존과 다를바가 없다.

 

헌데 순서가 다르다. 나는 name, face, age순으로 저장했는데, 출력값은 face, name, age순이다. 이는 앞서 말한데로 set에는 순서가 없기 때문이다. 그에 대해서는 아래에서 알아보자.

import java.util.*;

public class ex116 {
    public static void main(String[] args) {
        //맵(Map) : 키와 값으로 된 데이타 구조
        //        : Key(문자열형) - Value(객체,기본데이타형)
        //        : JSON, XML 데이타와 연결
        //        : 인덱스나 순서 없음.
        //Hash : 해쉬테이블을 이용한 Map데이타구조를 구현한 구현클래스
        //Hash Table : 데이타를 빨리 검색하기 위한 데이타구조
        Map<String, String> map = new HashMap<>();
        map.put("username", "hong"); //아이디
        map.put("password", "1234"); //비밀번호
        System.out.println( map );
        //Map(key-value)객체 = {  }로 표현
        //List객체 = [ ]로 표현

        System.out.println( map.get("username") );
        System.out.println( map.get("password") );

        //전체 순환
        //일반for문 안됨 - 인덱스없음

        //향상된 for문
        //Set타입으로 키 문자열들을 받아옴.
        Set<String> keys = map.keySet();
        for( String key : keys ){
            System.out.println( map.get(key) );
        }
        //이터레이터를 이용
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String key = it.next();
            System.out.println( map.get(key) );
        }
        //연습문제 58
        //맵(map) 연습문제
        //철수라는 학생이 데이타를 맵으로 설정해 보자.
        //map의 이름은 student라 하고
        //키값 age : 값 20
        //키값 height : 값 170
        //키값 name : "배철수" 로 데이타를 설정후
        //모든 Key Value를 출력하시오.
        //1.향상된 for문 사용
        //2.이터레이터 사용
        Map<String, String> student = new HashMap<>();
        student.put("age", "20");
        student.put("height", "170");
        student.put("name", "배철수");
        Set<String> keys2 = student.keySet();
        for(String key : keys2){
            System.out.println( student.get( key) );
        }
        it = keys2.iterator();
        while( it.hasNext() ) {
            String key = it.next();
            System.out.println( student.get(key) );
        }

        Map<String, Object> student2 = new HashMap<>();
        student2.put("age", 20);
        student2.put("height", 170);
        student2.put("name", "배철수");
        Integer age = (Integer)student2.get("age");
        Integer height = (Integer)student2.get("height");
        String name = (String)student2.get("name");
        Set<String> keys3 = student2.keySet();
        for(String key : keys3){
            System.out.println( student2.get(key) );
        }
    }
}

 

Set형식

set은 수학에서의 집합과 같다. 순서도 없고, 중복도 없다.

차집합과 교집합, 합집합을 모두 구할수 있고, 자료를 넣거나 뺄수도 있으나, 중복된 값은 애초에 들어가지지도 않는다.

 

add(): 요소를 추가하는 함수

retainAll() : 또 다른 set형식 자료와 교집합을 실행.

addAll() : 또 다른 set형식 자료와 합집합을 실행.

removeAll() : 또 다른 set형식 자료와 차집합을 실행.

 

* 주의 * 교집합, 합집합, 차집합 함수를 실행하고 나면 원본 값이 바뀌게 된다.

 

출력하면 []로 감싸져서 나온다.

 

또한 순환 역시 기존 리스트나 map과 똑같다. 다만 set역시 인덱스 번호가 없기에 for문은 이용 불가능하다.

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class ex117 {
    public static void main(String[] args) {
        //콜렉션 : 리스트, 맵, 셋(집합)
        //리스트 : 순차적으로 나열된 데이타, 인덱스있음. 추가/삭제 쉽다.
        //맵 : 키(Key)와 값(Value)로 구성된 데이타. 순서없음. 통신에 사용.
        //집합(세트) : 중복된 데이타를 허용안함. 순서없음. 집합데이타 처리에 사용.
        Set<String> set = new HashSet<>();
        set.add("홍길동");
        set.add("사임당");
        set.add("변사또");
        System.out.println( set );

        //중복허용을 하지 않기에, 같은데이타를 넣으면 추가 안됨.
        boolean isAdded = set.add("홍길동");
        System.out.println( isAdded );
        System.out.println( set );

        //전체 순환
        for( String name : set ){
            System.out.println( name );
        }
        Iterator<String> it = set.iterator();
        while( it.hasNext() ){
            System.out.println( it.next() );
        }

        //집합 연산 - 교집합,합집합,차집합
        Set<Integer> setA = new HashSet<>();
        Set<Integer> setB = new HashSet<>();
        setA.add(10);
        setA.add(20);
        setA.add(30);  //[10,20,30]
        setB.add(30);  //      [30,40,50]
        setB.add(40);
        setB.add(50);
        //교집합 [30]
        //setA.retainAll( setB ); //setA가 교집합으로 값 변경
        //System.out.println( setA );
        //합집합 [10,20,30,40,50]
        //setA.addAll( setB );
        //System.out.println( setA );
        //차집합 A - B = [10,20]
        setA.removeAll( setB );
        System.out.println( setA );

        //집합A가 집합B를 포함하고 있는가?
        //                 10 20        30 40 50
        System.out.println( setA.containsAll(setB) );
    }
}

 

재귀함수

다시 재, 돌아갈 귀. 

다시 처음으로 돌아간다는 뜻처럼, 함수의 끝에서, 다시 함수의 시작점으로 돌아가는 함수다.

프로그래밍적으로 말하자면, 결과값으로 자기 자신으로 호출하는 함수다.

함수의 실행문은 순차적으로 이뤄짐으로, 재귀함수를 벗어나면 그 아래 코드가 실행된다.

재귀함수는 각 함수가 호출될 때 마다 새로운 메모리 용량을 사용한다. 첫번째 재귀에서의 지역 변수, 반환주소값, 매개변수, 두번째 재귀에서의 지역변수, 반환주소값, 매개변수.... n번째 재귀에서의 지역변서, 반환주소값, 매개변수가 재귀를 벗어날때까지 차곡차곡 Stack형식으로 쌓이다, 탈출조건을 만족하게 되면 그제야 하나하나 수행되며 사라지는 것이다.

 

따라서 메모리 용량 초과, StackOverflowError 가 발생할 위험이 있다.

 

사용법은 함수의 return값에 자기 자신을 return하면 된다.

return값이 없는 void형식의 경우, 그냥 함수 내에 자기 자신을 실행시키면 된다. 이경우, 탈출 조건에 return;을 적으면 알아서 탈출이 된다.

public class ex118 {
    public static void main(String[] args) {
        //재귀함수 (Recursive Function)
        //   : 자기가 자기를 호출하는 함수
        System.out.println("메인함수 시작");
myFunc();
        System.out.println("메인함수 종료");
        return;
    }//main
    static intcount= 0;
    static void myFunc(){
        System.out.println("MyFunc 시작:"+count);
count++;
        //탈출조건
        if(count> 2){
count--;
            System.out.println("탈출1:count="+count);
            return;
        }
myFunc();
count--;
        System.out.println("탈출2:count="+count);
        return;
    }
}//class

아래는 재귀함수 문제다. 재귀함수는 for문과 달리 문자열을 포함해서 연산을 알아보기 쉽게 출력하는건 너무 어려운 것 같다.

public class ex119 {
    static int factorial( int n ){
        // return n * func( n - 1)
        //        1
        //        3 *        2 * 1
        //        4 *        3 * 2 * 1
        //        5 *        4 * 3 * 2 * 1
        if( n == 1 ) {
            System.out.println("n:1");
            return 1;
        }
        System.out.println( "n:"+n );
        System.out.println( "n-1:"+(n-1) );
        return n * factorial( n - 1 );
    }
    static int sum( int n ){
        if( n == 1 ) {
            System.out.println("n:1");
            return 1;
        }
        System.out.println( "n:"+n );
        System.out.println( "n-1:"+(n-1) );
        return n + sum( n - 1 );
    }
    static void printBin(int n){
        if( n < 2 ){
            System.out.print( n );
        }else{
            printBin( n / 2 );
            System.out.print( n % 2 );
        }
    }
    public static void main(String[] args) {
        //연습문제 59
        //재귀함수로 팩토리얼(Factorial)을 구현해보자.
        //int n = 5;
        //5! = 5*4*3*2*1
        //형식 : int result = factorial( n )
        int n = 5;
        int result = factorial( n );
        System.out.println( result );


        //연습문제 60
        //1부터 N까지의 합계를 출력하시오.
        // int n = 5;
        // sum( n )을 호출하면, 5+4+3+2+1 까지의 합이 반환된다.
        System.out.println( sum(n) );

        //연습문제 61 - 재귀함수
        // 십진정수를 2진수로 변환하여 출력하시오.
        // printBin( int n )
        // printBin( 10 ) 호출시, 1010으로 출력됨.
        //   2 )  10
        //         5 - 0
        //         2 - 1
        //         1 - 0
        printBin( 10 );

        //연습문제 62 - 문자열을 역으로 출력하기
        // 예) "abcde" => "edcba"
        System.out.println();
        reverseWord("abcde");
    }
    static void reverseWord(String str){
        if( str.length()==0 ){
            return;
        }
        else{
            //substring(인덱스) : 인덱스부터 마지막까지 문자열을 가져옴.
            //"abcde"
            //"bcde"
            //"cde"
            //"de"
            //"e"
            reverseWord(str.substring(1));
            //첫글자만 출력함.
            System.out.print(str.charAt(0));
        }
    }
}

 

 

 

프로그래머스를 풀고 있는데 문제가 이상한 경우도 보이고, 알고리즘을 모르면 절대 통과 못하는 문제도 존재해 정말 미칠것 같다. 알고리즘을 모르는건 차라리 낫다. 코드를 짜보고, 결과값이 다 맞더라도 시간 초과로 나오면 이리저리 고민을 해보다가 알고리즘 탓이라고 결론을 지으면 된다.

다만 문제가 이상한 경우에는 정말 답이 없다. 내 컴퓨터에서 테스트 예제와 똑같은 값을 넣고 돌렸을때는 정상적인 결과가 나오는데, 프로그래머스 사이트에서는 틀린 결과값이 나오는 것이다. 코드의 띄어쓰기 하나까지 모두 똑같은데 결과값이 다르게 나오니까 황당하기 그지없다. 그냥 포기하는게 그나마 정신 건강이라도 지키는 방법이 아닌가 싶다.

'공부 > java' 카테고리의 다른 글

16-17일 통신  (0) 2022.12.06
16일차 복습  (0) 2022.12.06
14일차 복습  (0) 2022.12.01
13일차  (0) 2022.11.30
자바(java)의 객체의 다형성  (0) 2022.11.29