// 하나씩 가져와서 결과 출력 for (inti=0; i < al.size(); i++) System.out.print(al.get(i) + " "); //1 2 3 5
Queue
구현클래스 : LinkedList, PriorityQueue
Map
구현클래스 : Hashtable, HashMap, TreeMap
키(Key), 값(Value)의 쌍으로 이루어진 데이터의 집합
함수 리스트 add(), remove(), clear(), size()
// 직접 위의 함수들 구현해보는 것도 좋은 공부다
private int[] nums, private int current;
String 문자열 클래스
이제야 눈에 보이는 스트링. 원시타입이 아니다 (헷갈리지 않게 조심)
자바에서 컬렉션으로 만들어 놓은 자료구조다
charAt(), compareTo(), concat(), indexOf(), trim(), toLowerCase(), toUpperCase(), substring, length(), isEmpty(), contains() 문자의 위치반환 없으면-1, matches(정규식) 등의 함수가 있다
스트링은 값이 달라질 때 새로운 객체를 만들어서 할당받는다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//자바에서 문자열1에서 문자열2를 포함한 갯수 찾기 ```java public int getCountInclude(String str1, String str2){ boolean include = true; int count = 0; include = str1.contains(str2); //포함되었는지 참,거짓 판단 while(include) { //포함되었다면 count++; int where = str1.indexOf(str2); //시작하는 위치 파악 str1 = str1.substring(where+str2.length()); System.out.println("str1 : "+str1); //방금 찾은 거를 빼고 남은 문자열을 부모로 업데이트 //substring 함수는 인자가 하나면 해당 위치부터 이후로 끝까지 자르는 함수 include = str1.contains(str2); } return count; }
- 뜻 : 포괄적인, 일반적인 - Object 를 만들면 사용(저장, 로드)하기전에 형변환 작업을 해줘야 하는 불편함이 존재한다. - 범용 컬렉션의 장점과 특화된 클래스의 장점을 모두 겸비한 템플릿. - Java5에서 추가 - 컬렉션, 람다식, 스트림, NIO(New IO)등에서 널리 사용되므로 제네릭을 이해하지 못하면 API 문서를 정확히 이해할 수 없다.
- 목적 - 자료형을 일반화. 정의시가 아니라 인스턴스 생성시 자료형을 결정하도록 한다. - 우리가 어떤 자료구조를 만들어서 배포하려고 할때 '여러 데이터타입을 지원하고 싶다.' 그러면 String에 대한 클래스, Integer에 대한 클래스 등등을 하나하나 타입별로 만들어줘야하나? 너무 비효율적이다.
- 제네릭은 이런 문제를 해결해준다. - 그렇게 강조했던 메소드의 정의와 호출 다시한번 보자. 어떤 이름으로 넘어오든지 정의한 영역내에서는 이 이름으로 쓰겠다는 뜻이었던가? - 마찬가지로 제너릭이라는 것은 어떤 데이터타입으로 넘어오든지 이 정의한 영역내에는 넘어온 그 데이터타입으로 쓰겠다 라고 하는것이다. - 즉 데이터형을 정의시 명시하지 않고 호출할때 정할 수 있도록 일반화 한것이다. - 클래스 내부에서 지정하지 않고 외부에서 지정도록 한 것이다. - 엄밀히 말하자면 컴파일때 해당 타입으로 캐스팅하는 것이다.
- 사용 예시 ```java import java.util.List; import java.util.ArrayList; public class 제네릭 { // 함수의 정의와 호출이 다르다. 파라미터와 아큐먼트는 다르다 public static void main(String[] args) { //일반 List list = new ArrayList(); list.add("hello"); String str = (String)list.get(0); System.out.println(str); //제네릭사용한 코드 < 데이터 타입> List<String> list2 = new ArrayList<String>(); list2.add("hello"); String str2 = list2.get(0); System.out.println(str2); } }
1 2 3 4 5 6 7
public class Box<T>{ // 클래스 뒤에 <T> 타입 파라미터를 명시했기 때문에 변수의 타입으로 사용 가능합니다. private T t; public T get() { return t; } public void set(T t){ this.t = t; } }
특징
보편적(암묵적)으로 매개변수의 타입으로는 아래의 이름을 쓴다. : Element : Key : Number : Type : Value
특정범위 내로 좁혀서 제한하고 싶다면 extends, super, ? 를 활용한다.
예를 들어서, 이면 T타입의 조상만 가능하고 K로 쓰겠다는 말이다.
문법
정의 : 클래스 또는 인터페이스 이름 뒤에 <타입파라미터>
생략하면 컴파일러가 제네릭 관련 문장에서 자료형의 이름을 추론한다. 이걸 다이아몬드 표시라고 한다.
타입 파라미터로 명시할수 있는 것은 참조타입만 가능하다.
여러 개의 타입변수는 쉼표로 구분하여 명시한다.
사용 : 본문에서 그 이름을 데이터타입처럼 사용하면 된다.
장점
런타임에 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지함
반환값에 대한 타입 변환(캐스팅) 및 타입검사에 들어가는 노력 줄어듬
코드의 재사용성이 높아
람다식(Lamda Expression)
자바8에 함수적 프로그래밍 기법으로 도입됨. 람다가 포함되면서 자바가 완전해졌다고 말하는 이가 있을 정도
익명함수를 생성하기 위한 식
y = f(x) 형태를 (타입 매개변수) -> { 실행문 } 으로 정의 좋든 싫든 자바의 람다식은 선택이 아닌 필수가 되었다
장점 : 1. 코드가 매우 간결해진다 2. 인스턴스 생성안하고 기능하나만 필요할 때 3. 컬렉션요소를 매핑해서 쉽게 집계할 수 있다
단점 : 1. 가독성이 안좋아진다
2.함수이름이 없기 때문에 stacktrace보고 디버깅하기 어렵다 3. 이 함수가 있음을 인식하기가 어렵다
결론 : 저런 단점들 때문에 고급개발자는 람다식 쓰지 말라 하는 사람 많다
코드가 3줄안에 끝나고 단 한번만 쓰임을 보장할 수 있고 행동이 명확할 때만 제한적으로 사용
publicclassGameTimerextendsThread{ int gameCount=0; publicGameTimer(int now) { //몇번째 게임에 만들어진 타이머인가 super(); this.gameCount = now; } @Override publicvoidrun() { for(int i=5; i>=1; i--){ /* 여러번 경기를 할때 어려움이 발생하는 지점 * inputCheck를 true로 하면 다음 타이머 작동안하고, * false로 하면 이전것이 살아있어서 남아있는 초를 출력하게 된다. * 타이머는 5초를 셀 뿐이다. 타이머의 제어는 타이머의 외부에서 일어나도록 하자. 객체지향적으로 */
publicclassGameTimerextendsThread{ int gameCount=0; publicGameTimer(int now) { //몇번째 게임에 만들어진 타이머인가 super(); this.gameCount = now; } @Override publicvoidrun() { for(int i=5; i>=1; i--){ /* 여러번 경기를 할때 어려움이 발생하는 지점 * inputCheck를 true로 하면 다음 타이머 작동안하고, * false로 하면 이전것이 살아있어서 남아있는 초를 출력하게 된다. * 타이머는 5초를 셀 뿐이다. 타이머의 제어는 타이머의 외부에서 일어나도록 하자. 객체지향적으로 */
//인스턴스가 하나 만들어 질때마다 조회수를 1 높이는 작업 publicclassCounter { staticintcount=0; // 정적화. 전역화 = 프로그램이 생성될때부터 꺼질때가지 유지 // 전역화 = 정의된 지역에 상관이 프로그램이 실행될때 생성되어 프로그램이 종료될때까지 // 인스턴스화 하지 않고 클래스를 컴파일할때 메모리에 올라간다 // 그때 올라가서 프로그램이 종료될 때까지 유지되니 '공유'라는 성질을 갖게 된다. publicCounter() { count++; System.out.println("생성됨. count : "+ count); } }
public class 배열 { // 9개의 서로 다른 자연수가 주어질 때, 이들 중 최댓값을 찾고 // 그 최댓값이 몇 번째 수인지를 구하는 프로그램을 작성하시오 // 예를 들어, 서로 다른 9개의 자연수 // 3, 29, 38, 12, 57, 74, 40, 85, 61 // 입력 : 9개의 자연수가 들어있는 배열 // 리턴 : 없음 // 본문 : 최댓값과 몇번째인지 로그로 출력 // 함수명 : printMaxValIdx
//세 개의 자연수 A, B, C가 주어질 때 //AxBxC 를 계산한 결과에 각각의 숫자가 몇 번씩 쓰였는지를 구하는 함수를 작성하시오 //예를 들어 A=150, B=266, C=427이라면 //AxBxC=150 x 260 x 427 = 17037300이 되고, //계산한 결과 1703730에 0이 3번, 1이 1번, 3이 2번, 7이 2번 쓰였다
//인풋 : 정수 3개 //리턴 : void //본문 : 쓰여진 숫자 : 쓰여진 갯수 출력 // //1. 3개의 자연수를 받은 함수 껍데기 만들기 //2. 3개의 수를 곱한 결과값 뽑기 //3. 0부터 9까지 반복하면서 각각의 수가 몇번 쓰였는지 파악하기 publicvoidnumCount(int a, int b, int c) { intmul= a * b * c; System.out.printf("%d x %d x %d = %d \n", a, b, c, mul); int[] arr= newint[10]; int[] printNum= newint[10]; for(int i=0; i< 10; i++) { // 예)1000을 1 0 0 0 형태로 배열에 담기 arr[i] = mul % 10; // 10으로 나눈 나머지 = 1의 자리 mul = mul / 10; System.out.printf("%d ", arr[i]); } System.out.println(""); for(int i=0; i<10; i++) { // 출력할 수를 printNum에 담는다 for(int j=0; j<10; j++) { // printNum의 n번째 자리를 숫자 n으로 인식 if(arr[i]==j) // 예)printNum의 1번째 자리가 2 = 1을 2번 사용함 printNum[j] += 1; } } System.out.println(); // 결과 출력 for(int i=1; i<10; i++) { if(printNum[i]!=0) { System.out.printf("%d 이 %d 개 사용되었다. \n", i, printNum[i]); } } } }
public class 배열나눠서오름차순출력 { //divideArrbyNum(int[] 인트형배열, int 기준숫자) //인트형 배열 안에서 기준숫자 이상인 엘리먼트 (요소=값하나)와 //기준 숫자 이하인 엘리먼트 2개로 나눠서 //각각 모든 요소를 오름차순으로 출력해보세요. //예) [1, 2, 3, 5, 7, 80, 150, 2022, 30534] //기준이 100이라면 //[1,2,3,5,7,80] / [150,2022,30534] 로 나뉨 //생각의 깊이 : 나눠서 정렬을 각각 하는거보다 처음에 정렬을 하면 한번만 하면 됨
- 원시형 8가지 type이 아니라면 (==) 는 주소값을 비교한다 - 이런 경우에 실제 값을 비교하려면 equals를 사용
> ==연산자와 equals()메소드의 가장 큰 차이점은 == 연산자는 비교하고자 하는 두개의 대상의 주소값을 비교하는데 반해 String클래스의 equals 메소드는 비교하고자 하는 두개의 대상의 값 자체를 비교한다는 것입니다. 기본 타입의 int형, char형등은 Call by Value 형태로 기본적으로 대상에 주소값을 가지지 않는 형태로 사용됩니다. 하지만 String은 일반적인 타입이 아니라 클래스입니다. 클래스는 기본적으로 Call by Reference형태로 생성 시 주소값이 부여됩니다. 그렇기에 String타입을 선언했을때는 같은 값을 부여하더라도 서로간의 주소값이 다릅니다. >
### 실습 - 배열
- 배열 선언법 정리 - ==와 equal 차이 비교 - 랜덤숫자 맞추기 게임 구현으로 응용
- 배열 연습 ```java package 배열; import java.util.Arrays; import java.util.Random; import java.util.Scanner; public class Main { public static void main(String[] args) { // 초기화 = 정의 후에 값 할당까지 한 번에 하는 것 int intArr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int[] intArr3; intArr3 = new int[]{1,2,3,4,5,6,7,8,9,10}; // Java 형식 // if(intArr == intArr3) { // System.out.println("같음"); // } String[] strArr1 = {"일", "이", "삼"}; // 스트링배열을 스트링으로 변환할 수 없습니다. int twoMension[][] = {{1, 2, 3}, {5, 10, 15}}; // System.out.println(twoMension.length); // System.out.println(twoMension[1]); // 향상된 for문. iterator = 반복할수 있는 객체 // iterator의 모든요소에 대해서 반복을 수행 // for(int i : twoMension[1]) { // System.out.print(i + " "); // } // System.out.println(""); int [][][] threeDemension = new int[2][4][6]; // 뒤에서부터 읽는다. // 2개짜리가 4개 있고 그런 놈이 6개있다. X // 6개짜리가 4개 있고 그런 놈이 2개있다. O // System.out.println(threeDemension.length); String[] 계절 = {"봄", "여름", "가을", "겨울"}; String[] 복사본 = 계절; // 배열의 복사. 그냥 =으로 하면 얕은 복사. String[] 진짜복사본; // String[] 진짜복사본2 = new String[]; boolean result = Arrays.asList(계절).contains("핵겨율"); // System.out.println(result); // if( 계절 == 복사본) { // System.out.println("복사본 : 같다"); // }else { // System.out.println("복사본 : 틀리다"); // } 진짜복사본 = 계절.clone(); // if( 계절 == 진짜복사본 ) { // System.out.println("진짜복사본 : 같다"); // }else { // System.out.println("진짜복사본 : 틀리다"); // } // 깊은 복사를 하기 위해서 반복문을 돌리든가, system.arraycopy() 메소드를 이용 // System.arraycopy(계절, 0, 진짜복사본2, 0, 계절.length); // if(계절 == 진짜복사본2) { // System.out.println("진짜복사본2 : 같다"); // }else { // System.out.println("진짜복사본2 : 틀리다"); // } // 영단어가 저장된 배열. 크기는 5. endDic // apple, banana, peach // 한글단어가 저장된 배열. 크기는 5. korDic // 사과, 바나나, 복숭아 // 한글랜덤단어를 보여주고 // 유저는 영단어를 치고 // 답이 맞는지 틀렸는지 판단 // System.out.println("키보드로 뭔가를 입력해보세요 : "); // Scanner sc = new Scanner(System.in); // 캐보드로 입력받기 위한 객체 생성 // String input = sc.next(); // System.out.println(input); } }
Dogmydog1=newDog(); // Dog클래스에서 mydog1 객체 생성 System.out.println(mydog1.name); // mydog1 이름 출력 Humankim=newHuman(); // Human클래스에서 kim 객체 생성 kim.taming(mydog1); // kim이 mydog1을 인자로 받아서 함수 싱행 } }
Humankim=newHuman(); // Human 클래스에서 kim 객체 생성 System.out.println(kim.name); // kim 이름 출력 kim.walk(); // kim의 함수 호출 // Dog dog1 = new Dog(); // Dog 클래스에서 dog1 객체 생성 // dog1.jump(); // dog1의 함수 호출 } }
Humankim=newHuman("아무개", 21); // Human 클래스에서 kim 객체 생성 System.out.println(kim.name); // kim 이름 출력 kim.walk(); // kim의 함수 호출 // Dog dog1 = new Dog(); // Dog 클래스에서 dog1 객체 생성 // dog1.jump(); // dog1의 함수 호출 } }
public class Main { public static void main(String[] args) { // System.out.println("헬로 월드"); int a = 5; double b = 7.5;
System.out.println(a+b); // call by value. 기본형. 값에 의한 참조 char x = 'a'; String y = "hello"; System.out.println(x+y); int myNum = a; myNum = myNum + 10; System.out.println("Mynum : " + myNum); System.out.println("a : " + a); // call by reference. 참조형. 주소에 의한 참조 String[] imgArr = {"🍕", "🍖"}; String[] stringArr = {"피자", "통닭", ""}; imgArr = stringArr; // 한글이 들어감 stringArr[2] = "아이스크림"; int i=0; System.out.println(imgArr[0]); System.out.println(imgArr[1]); System.out.println(imgArr[2]); // for(i=0; i<imgArr.length; i++) { // System.out.println(imgArr[i]); // } } }
연산자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package variable;
public class 연산자 { public static void main(String[] args) { int a = 100; int b = 333; System.out.println(a++ + ++b); // 100 + 334 = 434 // ++ 이나 -- 증감연산자가 // 뒤에있으면 사용한뒤 값을 변경 // 앞에 있으면 변경한 뒤에 사용 } }