1. 🔥 String클래스!

c언어와 다르게 자바는 문자열을 표현하기 위해 문자열 클래스를 가진다.

public class App {
    public static void main(String[] args) throws Exception {
        
        String str1 = "fkdl";
        String str2 = new String("fkdl");

        str1 += "hello" //새로운 메모리할당 후 그곳을 가르킴 "fkdl hello" 연산이 아님!!
    }   
}

위 코드에서 str1 과 str2는 같은 값을 가진다. str1은 ‘new String’를 생략해도 실행이된다.

포인터와 비슷한 개념으로 상수처럼 생긴 문자얼을 가르키게 된다.

특징

  1. immutale(변화가 불가능한): 즉 상수(Literal) 처럼 문자열의 내용은 변경 불가능하다.
  2. 비교 시 반드시 equals()사용 // ‘==’ 사용시 해시값이 같은지 확인

생성 방법

    String s = "Hello";
    String e = "Hello"; //같은 Hello를 가리킴 즉, 주소가 동일
  • 리터럴로 생성, JVM이 리터럴 관리, 응용프로그램 내에서 공유됨
String t = new String("Hello");   
String x = new String("Hello"); //서로 다른 주소를 가리킴
  • 힙 메모리에서 객체를 생성

1.1. StringBuffer 클래스

스트링 버퍼는 링크 리스트처럼 동작한다.

StringBuffer sb =  new StringBuffer("a"); //생성 및 초기화  
sb.append(" pencil"); //뒤에 문자열 붙임
sb.insert(2, "nice "); // 중간에 문자열 삽입
sb.replace(2, 6, "bad"); //문자열 치환

1.2. StringTokenizer 클래스

  • 하나의 문자열을 여러 문자열로 분리 이때 분리할 때 사용되는 기준 문자를 구분문자라고 한다.
String query = "name=kitae&add=seoul&age=21";
StringTokenizer st = new StringTokenizer(query, "&");

토근: 구분 문자로 분리된 문자열

split()으로 구현가능

import java.util.StringTokenizer;

public class StringTokenizerEx {
 public static void main(String[] args) {
  StringTokenizer st = new StringTokenizer("홍길동/장화/홍련/콩쥐/팥쥐", "/");
  while (st.hasMoreTokens()) 
   System.out.println(st.nextToken());
 }
}
  • nextToken()으로 하나씩 뽑아냄 언제까지? hasMoreTokens()토큰이 존재할 때 까지

구조를 봐도 링크리스트형태로 구현된걸 얼추 알 수 있다.


1.3. Math 클래스

java.lang.Math패키지에 들어있으며 모든 메서드는 static타입이다. 즉, 클래스이름으로 바로 호출해야함

ex) static double abs(double a) -> abs(인자)

종류

  1. abs
  2. cos
  3. sin
  4. exp(지수)
  5. ceil/floor
  6. max/min
  7. random(1~0반환)
  8. sqrt

2. 🔥 Generic Programming

자바가 가지고 있는 세가지 특성 객체지향, 구조적 예외처리, 일반적 프로그래밍

  • 데이터타입에 종속되지 않고 독립적인, 코딩할 떄 데이터 타입이 결정되지 않고 실행될 때 지정되는 프로그래밍 기법이다..!!

다양한 타입의 객체를 동일한 코드로 처리하는 기법, 컬렉션 라이브러리에 많이 사용된다.

//version 1
class Box{
    private int data;
    Box(int in){
        data = in;
    }
    int getData(){
        return data; 
    }
    void setData(int in){
        data = in;
    }
    @Override
    public String toString(){
        String str = "";
        str += data;
        return str;
    }
}

public class App {

    public static void func(Object o){
        System.out.println(o);
    }

    public static void main(String[] args) {

        String str = "sejong";
        func(str);

        Box a = new Box(10);
        func(a);
 }
}

위에 코드를 분석해보면 Box라는 클래스를 main클래스에서 호출(func)으로 이 가능하다.

  • 그렇다면 Box데이터 타입이 float로 바뀐다면 Box클래스 내부의 데이터 타입을 전부 float로 하나씩 바꿔야 한다.

이때 도입된게 바로 제네릭 프로그래밍의 탬플릿 기능이다.

//version 2
class Box <T>{
    private T data;
    Box(T in){
        data = in;
    }
    T getData(){
        return data; 
    }
    void setData(T in){
        data = in;
    }
    @Override
    public String toString(){
        String str = "";
        str += data;
        return str;
    }
}

public class App {

    public static void func(Object o){
        System.out.println(o);
    }

    public static void main(String[] args) {

        String str = "sejong";
        func(str);

        Box <Integer>a = new Box<Integer>(10);
        func(a);
 }
}

여기서 T는 아직 확정되지 않은 데이터타입을 가르킨다.

  • 이때 주의해야 하는 점은 함수를 호출할 때 데이터 타입을 정하게 되는데 그 부분은 데이터 타입의 클래스 즉, Wrapper클래스를 사용해야 한다.

이러한 형태가 탬플릿의 기본적인 문법이다.

한가지만 사용가능한 것이 아니라 여러가지 템플릿 변수로 사용가능하다.

class Pair <K, V>{ //두가지 항목
    K key;
    V value;
    Pair(K k, V v){
        key = k;
        value = v;
    }
    @Override
    public String toString(){
        String str;
        str = "key = "+key+", value = "+value;
        return str;
    }
}

// 호출 부분
Pair<Integer,String> b = new Pair<>(123, "이정안"); 
Pair<Integer,String> b = new Pair<Integer, String>(123, "이정안");
// new 뒤에 클래스 명은 생략 가능..!
  • 이처럼 자바에서는 명시적이면 생략 가능한 문법이 존재한다..!
public static void main(String[] args) {

    Box <String>a = new Box<String>("fkdl");
    func(a);
    
    String s = a.getData(); // 언방식

    Box c = new Box("4878"); //<Object> 생략 명시적, 업캐스팅 / Raw타입
    func(c);

    String ss = c.getData(); // 언방식 오류 위에서 <Object>로 만들어져서..
}

Raw타입

위에서 다룬 명시적인 부분은 생략 가능한 점을 이용하여 Box c = new Box("4878"); 부분에서 String을 생략했다. 내부적인 구조는 가 생략된 형태이다.(Raw 타입)

  • 위 코드를 정상적으로 실행시키고 싶다면 다운캐스팅을 해야한다. String ss = (String)c.getData();

업캐스팅, 언방식, 다운캐스팅을 사용하기 보다 작성자가 명시적으로 데이터 타입을 적어주는 편이 더욱 가독성 측면에서 이점이 있다.

그렇다면 박싱이란?

public class App {

    public static void func(Object o){
        System.out.println(o);
    }

    public static void main(String[] args) {

        String str = "sejong";
        func(str);

        int a = 10;
        func(a);
 }
}

위 코드에서 func메서드에는 object(클래스)로 인자를 받기 때문에 String형은 통과될 수 있지만 int형은 데이터타입이기 때문에 실행될 수 없다.

  • 하지만! 박싱 and 언방식으로 인해서 자동으로 int -> Interger로 변환시켜 클래스로 넘겨주기 때문에 올바르게 실행된다..!

2.1. 제네릭 메서드

위에선 클래스에서만 제네릭을 사용하여 데이터 타입(사실은 클래스)을 일반화 하였는데 사실 메서드에서도 일반화가 가능하다.

class MyArrayUtil{
    static <T> T getAt(T []arr, int index){
        return arr[index];
    }
}
---메인부분
  String [] array = {"123","453","8895"};
  System.out.println(MyArrayUtil.getAt(array, 2)); //(MyArrayUtil.<String>getAt(array, 2)) <String>생략가능
  • 위에서 처럼 제네릭 메서드는 String이 생략 가능하다..!

한정된 타입 매개변수(제한)

또한 인터페이스를 활용한 매개변수(들어오는 클래스)에 제한을 걸 수 있다.

  • 예를 들어 어떠한 인터페이스의 메서드를 호출한다면 해당 즉, 들어오는 클래스는 그 인터페이스(연결단자라고 생각)을 상속받아야지만 로 들어올 수 있다.

<T extends Comparable> 즉, Comparable이라는 인터페이스를 구현해야지만 해당 클래스로 입장 가능하다. 왜냐하면 메서드 내부에서 해당 인터페이스의 메서드를 사용하기 때문..!

2.2. 제네릭과 상속

Object obj = new Object(); 
Integer i = new Integer(10);
obj = i; //업캐스팅 가능! 

Box<Number> box = new Box<Number>();
box.add(new Integer(10)) // 가능!
box.add(new Float(10)) // 가능!

Box<Number> a = new Box<Number>();
Box<Integer> b = new Box<Integer>();

a != b // a와 b는 아무관계가 없다..!
  • 위에서 Number(인터페이스)는 Object로 상속받은 서브클래스이자 Interger, Float등등..의 수퍼클래스이다..!

이말은 object로 업캐스팅이 가능하다는 뜻인데 그렇다면 서로의 관계가 이어져 있다고 볼 수 있다.

하지만 a와 b는 아무런 관계가 없다고 할 수 있는데 그 이유는…!

  • Box<Number> a = new Box<Number>();로 객체를 생성하는 순간 컴파일러가 완전히 새로운 형식을 만들어 낸것이기 때문에 기존의 탬플릿 클래스와 현재의 탬플릿 클래스는 아무런 연관이 없다..!

*즉. Integer가 Number의 자식이지만 Box와 Box는 자식이 아니다..!*

제네릭 클래스의 상속

제네릭 클래스간의 상속 또한 가능하다.

2.3. 정리

그렇다면 제네릭 프로그래밍의 이론이라면 데이터를 object형으로 선언하여 받고 되돌려주는 부분이 왜 위험할까?

  • object형이라면 그 데이터로 다른 작업을 할때 다시 다운캐스팅해야하는 위험성을 가지게 된다.

3. Collection

자바에선 자료구조를 다루기 위해 탬플릿 라이브러리를 제공하는데 그 라이브러리의 이름이 콜렉션이다..!(인터페이스의 이름)

c++의 STL(container)와 같은 것

  • 시작하기에 앞서 지금 부터 배우는 자료구조들은 전부 인터페이스를 공유한다.

데이터 추가, 삭제, 검색, 추출등등..(기능들 즉, 인터페이스를 공유)

즉, 모든 메서드의 이름이 같을 수 밖에 없다..!

3.1. 컬렉션 종류 && 인터페이스 메서드

인터페이스!

인터페이스 설명
Collection 모든 자료구조의 부모 인터페이스
Set 집합(중복 x)을 나타내는 자료구조
List 순서가 있는 자료 구조(중복 o)
Map 키와 값들이 연관되어 있는 사전과 같은 자료구조
Queue FIFO의 구조를 가지는 자료구조

메서드

메서드 설명
___ 기본 연산
int size() 원소의 개수를 반환
boolean isEmpty() 공백상태이면 true반환
boolean contains(Object obj) obj를 포함하고 있으면 true반환
boolean add(E element) 원소 추가
boolean add(Object obj) 원소 삭제
Iterator <E> iterator(); 원소 방문
___ 벌크 연산
boolean addAll(Collection<? extends E> from) c에 있는 모든 원소 추가
boolean containsAll(Collection<?> c) c에 있는 모든 원소가 포함되어 있으면 true
boolean removeAll(Collection<?> c) c에 있는 모든 원소 삭제
void clear() 모든 원소 삭제
___ 배열 연산
Object[] toArray() 컬렉션을 배열로 변환
<T> T[] toArray(T[] a); 컬렉션을 배열로 변환

3.2. vector

vector클래스는 배열을 구현해놓은 자료구조이다. (array를 대체)

import java.util.Vector; // Vector클래스 import!
//import java.util.*; 가능
 
public class App {
    public static void main(String[] args){

        Vector<Integer> arr = new Vector<Integer>();//크기를 정할 필요가 없음
        //탬플릿 클래스이기 때문에 클래스만 지정가능 즉, Wrapper클래스사용
        arr.add(Integer.valueOf(10)); //값 생성
        arr.add(5); //박싱 언방식 기능 사용하여 자동 Integer형 변환
        arr.add(30);

        System.out.println("size = "+arr.size()); //사이즈 반환
        for(int i = 0; i<arr.size(); i++)
            System.out.println(arr.get(i)); //값 추출
        for(int i = 0; i<10;i++)
            arr.add((int)(Math.random()*100));
        
        System.out.println("-----------------");
        for(Integer e : arr) //foreach도 Integer형으로 사용가능
            System.out.println(e);
    }
}
  • 맨 처음 객체를 생성하면(arr) 해당 vector의 형태는 디폴트사이즈로 만들어지게 된다.

이후 값 추가를 통해 디폴트 사이즈의 크기를 넘어가게 되면 50% or 2배를 하여 크기를 키운다.

  • arr의 인자값은 Integer(클래스)로 만들었기 때문에 값을 넣어줄때 arr.add(Integer.valueOf(10));를 사용해야 하지만 박싱기능을 사용하여 해당 인자값을 그대로 전달 가능하다.

ex) arr.add(5);

  • 위에서 말한 인터페이스를 공유하기 때문에 size,get의 메서드는 다른 자료구조에서도 공통적으로 작동한다..

추가!

arr.remove(0); // 0번째 인덱스 삭제
arr.add(2,1234); // 2번 인덱스에 1234값 추가

Vector<String> strArr = new Vector<>(); //앞의 String의 탬플릿 인자값이 뒤에 자동 매핑
strArr.add("APPLE");
strArr.add("BANANA");

Vector vc = new Vector(); //lower타입 어떠한 객체도 받을 수 있음..! 추천하지 않음
  • 자료구조에서 알 수 있듯이 삭제와 인덱스 추가는 굉장히 귀찮은 일이라고 할 수 있는데 콜렉션에서는 이런 기능까지 쉽게 제공한다.

3.3. 콜렉션을 위한 자바 인터페이스와 클래스

앞서 설명한 것 처럼 최상위 부모 클래스는 Collection 탬플릿 인터페이스로 존재 내부에는 추가, 삭제, 추출등의 기능이 있다.

  • Vector로 설명하자면 Collection을 상속 받은 인터페이스 List가 존재하고 List인터페이스를 상속받은 클래스 Vector가 존재한다..!

List인터페이스는 Collection의 확장개념이며 Vector, ArrayList, LinkedList에 사용할 수 있는 메서드를 공유한다.

Vector는 사실 노후된 기능(병렬화에 안전하기 때문에 해당기능으로 사용)으로 잘 사용하지 않으며 확장개념인 ArrayList를 사용한다.

  • 그렇다면 List인터페이스를 공유하니 메서드의 이름도 같고 사용방식도 같으니 클래스만 바꾸면 사용가능하나?
//타입매개변수를 지니는 제네릭 클래스

Vector<Integer> arr = new Vector<Integer>();//현재 Vector
ArrayList<Integer> arr = new ArrayList<Integer>(); //윗줄 지우고 사용해도 아래 코드 전혀 문제없음
LinkedList<Integer> arr = new LinkedList<Integer>(); 
  • 원리는 달라도 얻어지는 결과는 같다..!

자료구조에서 배운내용 처럼 ArrayList는 search에 용이하나 추가, 삭제에 불리하고 LinkedList는 search는 불리하나 삭제,추가가 유리하다.

주의

for(int i = 0; i<arr.size(); i++)
    System.out.println(arr.get(i)); //arrlist 상관없음, Linkedlist최악
  • LinkedList에서는 get의 사용을 최대한 줄이는게 베스트인데 위와 같은 코드는 LinkdList에 취약한 코드이다.(다시 맨처음으로 돌아가서 다시 찾아야함)

개선

for(Integer e : arr)
    System.out.println(e);
  • 이처럼 foreach문으로 접근한다면 그다음, 그다음을 순차적으로 접근할 수 있다.

3.4. ArrayList

배열과 똑같은 구조를 가진 콜렉션이다. 벡터와 마찬가지로 크기는 자동으로 계산하기 때문에 따로 고려할 필요가 없다.

  • 추가 연산 indexOf
int index = list.indexOf("APPLE"); //APPLE의 인덱스 반환

정리

  1. 크기를 신경쓸 필요가 없다, 기본 메서드들(추가, 삭제등)이 전부 구현되어 있어서 간편하다.
  2. ArrayList의 부모 인터페이스는 List가 있고 List는 Collection을 상속받았다.
  3. 인터페이스 참조변수를 통한 업캐스팅이 가능하다. 따라서 다형성을 사용할 수 있다.
  4. 반복처리는 반복자, for문을 통해 사용가능하다.

3.5. LinkedList

삽입이나 삭제시 전체적인 이동이 이루어지지 않는 가장 큰 이점을 지닌 List이다.

  • 위에서 설명한 것 처럼 linkedlist와 arraylist의 기본적인 차이점만 알면 문제없이 사용가능하다.
import java.util.*; //유틸패키지 전부가져옴

public class App {
    public static void main(String[] args){

        LinkedList<String> strArr = new LinkedList<>(); 
        strArr.add("APPLE"); //뒤에 추가가 기본
        strArr.add("BANANA");
        strArr.add("fjdk");
        strArr.add("fkdl");
        strArr.add("jeong");
        strArr.add("lee");

        strArr.remove(strArr.indexOf("fkdl")); 
        strArr.remove("lee"); //위의 과정이 자동으로 이루어짐

        for(String e : strArr)
            System.out.println(e);
        
        // *주의* 매우 위험한짓
        int i = 0;
        for(String e : strArr){ //foreach문은 앞에서 말한것 처럼 기본적으로 readonly의 성격을 가짐
            if(e.equals("APPLE") == true)
                strArr.remove(i); // 반복을 돌며 크기가 변해버림
            i++;
        }
    }
}

3.6. 반복자(접근자)

기본개념은 c언어의 임시포인터변수와 동일하다. 해당 리스트에 영향을 주지않고 새롭게 가르켜서 값을 수정, 변경할 수 있다.

Iterator it = strArr.iterator(); //이미 선언된 list의 iterator()메서드를 호출하여 만든다.
// 현재는 low데이터 타입이기 때문에 탬플릿 매개변수를 줘서 명확하게 해준다. 
Iterator<String> it = strArr.iterator();
  • 따라서 c언어와 유사하게 링크드 리스트에서 값을 삭제, 추가할 때에는 반복자(Iterator)를 사용해야한다.
import java.util.*;

public class App {
    public static void main(String[] args){

        LinkedList<String> strArr = new LinkedList<>(); 
        strArr.add("APPLE");
        strArr.add("BANANA");
        strArr.add("fjdk");
        strArr.add("fkdl");
        strArr.add("jeong");
        strArr.add("lee"); //값 추가
            
        Iterator<String> it = strArr.iterator(); //반복자 선언 및 할당
        while(it.hasNext()) { //다음 노드가 null이 아닐때 까지
            String e = it.next(); //값을 반환 후 다음노드로 이동
            if(e.equals("APPLE")) 
                it.remove();//반환된 값을 삭제
        }
        
        for(String e: strArr) {
            System.out.println(e);
        }
    }
}
  • 아까 다룬 for문으로 반복하며 linkedlist의 메서드를 사용해서 지우면 안전하게 값을 추가, 삭제할 수가 없다.

따라서 반복자를 사용해야한다.!

  • it.hasNext() c언어의 포인터와 문법이 거의 똑같은 형태이다. 즉, null을 가리키게 되면 종료
  • String e = it.next(); 다음 노드로 이동하면서 현재 노드의 값을 반환
  • it.remove(); 중요 값을 삭제하는데 현재 가르키고 있는 값을 삭제하는 것이 아닌 안전하게 전에 반환되었던 값을 삭제

따라서 이러한 반복자를 사용하면 안전하게 값을 참조할 수 있다.

추가++

  • 만약 사용자가 뒤부터 참조하여 접근하고 싶다면?
ListIterator<String> lit = strArr.listIterator(strArr.size()); //인자값을 size..
while(lit.hasPrevious())
    System.out.println(lit.previous());

해당 형식만 바꿔서 호출하면 문제없다.!

  • 사용자가 배열을 리스트행태로 바꾸고 싶다면?
List<String> list = Arrays.asList(new String[size]);

리스트의 형태로 치환가능!

어떠한 경우에 Linkedlist를 사용해야하는가

데이터의 삭제나 추가가 빈번하게 일어나는 경우! 하지만 get()은 사용하지 않는다! 속도의 문제와 데이터를 불안정하게 접근하기 때문 따라서 반복자(iterator)를 사용하여 접근하면 된다..!

3.7. HashMap

사전과 같은 형태로 저장되어 있는(key, value) 형태의 자료구조

4. Collections 클래스의 활용

자바에서는 sort()같은 함수(즉, 메서드)들은 java.util패키지에 포함되어 있는 Collections(s주의!)를 import하면 사용할 수 있다.

  • 모든 메서드는 유틸함수처럼 정적메서드로 구현되어 있어 사용에 용이함

sort,reverse,max,min,binarySearch..등등

import java.util.*;

//비교하기 위해 implements(인터페이스 상속) Comparable즉, 어떠한 기준으로 정렬할지 정해줘야함
class MySortablePosition implements Comparable<MySortablePosition>{ //템플릿을 활용하여 비교하게 될 대상을 설정가능
 int x, y;
 MySortablePosition(int _x, int _y){
  x = _x;
  y = _y;
 }
 @Override
 public String toString() {
  String str = "POS ("+x+","+y+")";
  return str;
 }
 int getDist2() { //원점에서의 거리
  return x*x+y*y;
 }
 @Override
 public int compareTo(MySortablePosition o) {
  return getDist2() - o.getDist2(); //현재 나. 그리고 다음대상을 비교
 }
}


public class App {
    public static void main(String[] args){
        //ArrayList(배열)로 생성 마찬가지로 크기 설정 X
        ArrayList<MySortablePosition> ptArr = new ArrayList<>();
  
        //값 설정
  ptArr.add(new MySortablePosition(1,2));
  ptArr.add(new MySortablePosition(3,4));
  ptArr.add(new MySortablePosition(1,3));
  ptArr.add(new MySortablePosition(4,2));
  ptArr.add(new MySortablePosition(2,3));

        //정렬 호출 Collections(s주의).sort -> Comparable상속 받아야 사용가능
  Collections.sort(ptArr);
  
  for(var e : ptArr) {
   System.out.println(e);
  }
  
  var max = Collections.max(ptArr); //마찬가지로 max값 찍기
  System.out.println("max = " +max);

    }
}

태그: ,

카테고리:

업데이트:

댓글남기기