본문 바로가기
☕ Java 웹 프로그래밍/Java

[프로그래머스] Java(자바) 중급 | Part 3. java.util 패키지

by 일단연 2023. 5. 13.

 java.util 패키지 

유용한 클래스들을 많이 가지고 있는 패키지

  • Date, Calendar 클래스: 날짜와 관련
    • Date클래스는 지역화를 지원하지 않음
      • 지역화(Localization): 국가별로 현재 날짜와 시간은 다를 수 있음
    • 이런 문제를 해결하기 위하여 나온 클래스가 Calendar클래스(Java 1.1에 등장)
    • 지역화와 관련된 클래스들은 Locale로 시작되는 이름을 가진 클래스들( Java 1.1 이후에 등장)
      • Locale, Locale.Builder, Locale.LanguageRange
  • Collection, Set, List, Map 인터페이스: 자료구조 즉, 컬렉션 프레임워크와 관련된 인터페이스
  • Java API에서 deprecated: 더이상 지원하지 않는 기능이니 사용하지 않는 것이 좋다는 의미

 

 컬렉션 프레임워크 

java.util 패키지에는 자료를 다룰 수 있는 자료구조 클래스가 다수 존재함

 

자료구조 클래스들 = 컬렉션 프레임워크

  • 자료구조: 자료를 저장할 수 있는 구조

 

컬렉션 프레임워크(collection framework)

  • 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합
  • 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현한 것
  • Java의 인터페이스(interface)를 사용하여 구현됨
  • 주요 인터페이스
    • List 인터페이스
    • Set 인터페이스
    • Map 인터페이스

 

주요 인터페이스 간의 상속 관계

  • <E>나 <K, V>라는 것은 컬렉션 프레임워크를 구성하는 모든 클래스가 제네릭으로 표현되어 있음을 알려줌

 

Collection인터페이스 - 중복 O, 순서 X

  • 컬렉션 프레임워크에서 가장 기본적인 동작들을 정의하고, 그것을 메소드로 제공
  • ‘여기에 자료가 있다’라는 것을 표현
  • 중복도 허용하고, 자료가 저장된 순서도 기억하지 못함
  • List와 Set 인터페이스의 많은 공통된 부분을 Collection인터페이스에서 정의하고, 두 인터페이스는 그것을 상속받음
  • Collection인터페이스의 메소드
    • Collection이 가지고 있는 대표적인 메소드는 add( ), size( ), Iterator( ) 메소드
메소드 설명
boolean add(E e) 해당 컬렉션(collection)에 전달된 요소를 추가 (선택적 기능)
int size( ) 해당 컬렉션 요소의 총 개수를 반환
Iterator<E> iterator( ) 자료를 하나씩 꺼내기 위한 iterator 인터페이스를 반환
void clear( ) 해당 컬렉션의 모든 요소를 제거 (선택적 기능)
boolean contains(Object o) 해당 컬렉션이 전달된 객체를 포함하고 있는지를 확인
boolean equals(Object o) 해당 컬렉션과 전달된 객체가 같은지를 확인
boolean isEmpty() 해당 컬렉션이 비어있는지를 확인
boolean remove(Object o) 해당 컬렉션에서 전달된 객체를 제거 (선택적 기능)
Object[] toArray( ) 해당 컬렉션의 모든 요소를 Object 타입의 배열로 반환
  • 저장된 순서를 기억하지 못하기 때문에 "첫 번째 자료를 달라, 두 번째 자료를 달라"와 같은 기능을 가질 수 없음

  • 대신, 저장된 자료를 하나씩 꺼낼 수 있는 Iterator라는 인터페이스를 반환
    • Iterator 인터페이스의 메소드
      • hasNext( )메소드: 꺼낼 것이 있는지 없는지 살펴봄
      • next( )메소드: 하나씩 자료를 꺼낼 때 사용

 

Set인터페이스: Set<E> - 중복 X, 순서 X

  • Collection인터페이스를 상속받음
  • 순서를 기억하지 못하지만, 중복은 허용하지 않는 자료구조
  • add(Object) 메소드: 같은 자료가 있으면 false, 없으면 true를 반환 (중복을 허용하지 않는 Set의 속성)
  • 구현 클래스: HashSet, TreeSet

 

List인터페이스: List<E> - 중복 O, 순서 O

  • Set인터페이스와 마찬가지로, Collection인터페이스를 상속받음
  • 순서는 기억하지만, 중복은 허용하는 자료구조
  • get(int) 메소드: 0번째, 1번째, …, n번째의 자료를 꺼낼 수 있음 (순서를 기억하는 List의 속성)
  • 구현 클래스: Vector, ArrayList, LinkedList, Stack, Queue

 

Map인터페이스: Map<K, V> - Key중복 X, Value중복 O, 순서 X

  • Key와 Value의 한 쌍으로 이루어지는 자료구조
  • put(Object, Object) 메소드를 이용해 key와 value를 함께 저장
  • key를 매개변수로 받아들이는 get(Object) 메소드를 이용해 원하는 값을 꺼냄
  • 순서를 기억하지 못함
  • Key엔 중복을 허용하지 않지만, Value는 중복될 수 있음
  • keySet( ) 메소드: 자신이 가지고 있는 모든 Key들에 대한 정보를 읽어들일 수 있는 Set을 반환(Key의 중복을 허용하지 않는 Map의 속성)
  • 구현 클래스: HashMap, TreeMap, Hashtable, Properties

 

컬렉션 클래스(collection class)

  • 컬렉션 프레임워크에 속하는 인터페이스를 구현한 클래스
  • 컬렉션 프레임워크의 모든 컬렉션 클래스는 List와 Set, Map 인터페이스 중 하나의 인터페이스를 구현
  • 클래스 이름에도 구현한 인터페이스의 이름이 포함되므로 바로 구분할 수 있음
  • Vector나 Hashtable과 같은 컬렉션 클래스는 예전부터 사용해 왔으므로, 기존 코드와의 호환을 위해 아직도 남아 있음
  • But, 기존에 사용하던 컬렉션 클래스를 사용하는 것보다는 새로 추가된 ArrayList나 HashMap 클래스를 사용하는 것이 성능 면에서도 더 나은 결과를 얻을 수 있음

 Generic 

Box 클래스

public class Box {
  private Object obj; //필드의 데이터타입: Object 클래스

  public void setObj(Object obj){  //필드의 값을 설정하는 메소드
    this.obj = obj; //필드와 매개변수의 이름이 같으니 this 사용
  }

  public Object getObj(){          //필드의 값을 반환하는 메소드
    return obj;
  }
}

 

BoxExam 클래스

public class BoxExam {
  public static void main(String[] args) {
    Box box = new Box();
    box.setObj(new Object());  //setObj 메소드에 매개변수로 Object 객체를 생성해 넣어줌
    Object obj = box.getObj(); //참조변수 box가 가지고 있는 Object 클래스를 리턴해줌

    box.setObj("hello");       //box에 String 값을 넣을 수도 있음
    //box에 값을 넣을 때 Object 타입을 썼으니, box에서 값을 꺼내서 사용할 떄도 리턴타입이 Object
    String str = (String)box.getObj(); //Object를 String으로 다운캐스팅하는 거니까 명시해줘야 함
    System.out.println(str);

    box.setObj(1);             //box에 int 값을 넣을 수도 있음
    int value = (int)box.getObj();
    System.out.println(value);
  }
}
  • Box는 매개변수로 Object를 하나 받아들이고(setObj 메소드), Object를 반환(getObj 메소드)
  • 매개변수로 Object를 받아들일 수 있다는 것은 Object의 후손(String, int 등)이라면 무엇이든 받아들일 수 있다는 것
  • box에 값을 넣을 때 Object 타입을 썼으니, box에서 값을 꺼내서 사용할 떄도 리턴타입이 Object
    • Object를 String으로 다운캐스팅하는 거니까 명시해줘야 함
      • String str = (String)box.getObj( );

 

Java 5에는 Generic이라는 문법이 사용됨으로써 인스턴스를 만들 때 사용하는 데이터타입을 지정하는 문법이 추가

  • Java 5 이전에는 여러 타입을 사용하는 대부분의 클래스/메소드에서 인수나 리턴값으로 Object 타입을 사용해 모든 데이터타입의 객체가 들어갈 수 있음
  • But, 꺼내서 사용할 때 반환된 Object 객체를 다시 원하는 타입으로 타입 변환해야 하며, 이때 오류가 발생할 가능성도 존재
  • Java 5부터 도입된 제네릭을 사용하면 컴파일 시에 미리 타입이 정해지므로, 타입 검사나 타입 변환과 같은 번거로운 작업을 생략할 수 있음

 

Generic을 이용하여 수정한, Box 클래스

public class Box<E> {
  private E obj;
  public void setObj(E obj){
    this.obj = obj;
  }

  public E getObj(){
    return obj;
  }
}
  • 클래스 이름 뒤에 <E>가 제네릭을 적용한 것
    • Box는 가상의 클래스 E를 사용한다는 의미
    • 꼭 E여야 할 필요는 없음
  • Object를 받아들이고 리턴하던 부분이 E로 변경. E는 실제로 존재하는 클래스는 아님.

 

Generic을 이용하여 수정한, Box를 이용하는 BoxExam클래스

public class BoxExam {
  public static void main(String[] args) {
    Box<Object> box = new Box<>();
    box.setObj(new Object());
    Object obj = box.getObj();

    Box<String> box2 = new Box<>();
    box2.setObj("hello");
    String str = box2.getObj();
    System.out.println(str);

    Box<Integer> box3 = new Box<>();
    box3.setObj(1);
    int value = box3.getObj();
    System.out.println(value);
  }
}
  • 참조타입에 <Object> , <String>, <Integer>가 있는 것을 볼 수 있음
  • 첫번째는 Object를 사용하는 Box 인스턴스를 만들겠다는 의미
  • 두번째는 String을 사용하는 Box 인스턴스를 만들겠다는 의미
  • 세번째는 Integer를 사용하는 Box 인스턴스를 만들겠다는 의미

 

Generic을 사용함으로써 가상의 타입으로 선언하고, 사용 시에는 구체적인 타입을 설정함으로써 다양한 타입의 클래스를 이용하는 클래스를 만들 수 있음

 

Generic을 사용하는 대표적인 클래스는 컬렉션 프레임워크와 관련된 클래스

  • Set, List, Map 인터페이스의 컬렉션 클래스

 

제네릭(Generic)

  • 데이터의 타입(data type)을 일반화한다(generalize)는 의미
  • 클래스나 메소드에서 사용할 내부 데이터 타입을 클래스 외부에서 사용자의 필요에 의해 컴파일 시 미리 지정하는 방법 (컴파일 시 미리 타입 검사(type check) 수행)
  • 제네릭의 장점
    • 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있음 (잘못된 타입이 들어오는 걸 컴파일 단계에서 방지)
    • 클래스 외부에서 타입을 지정해주기 때문에 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있음 (관리가 편함)
    • 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아짐
  • 제네릭의 타입
    타입 설명
    <T> Type
    <E> Element
    <K> Key
    <V> Value
    <N> Number
    <?> 모든 타입 가능
    (= <? extends Object>)
    • 반드시 한 글자일 필요도, 설명과 반드시 일치해야 할 필요도 없음 (예: <Ele>도 가능)
    • But, 대체로 위와 같이 사용

 

제네릭의 선언

  • 클래스 뒤에 <타입>을 적고, 타입이 들어가는 자리에 꺽쇠 안에 들어간 타입을 적어주면 됨
class MyArray<T> {
  T element;
  void setElement(T element){
    this.element = element;
  }
  T getElement(){
    return element;
  }
}
  • 예제의 'T'를 타입 변수(type variable)라고 하며, 임의의 참조형 타입을 의미
  • 꼭 'T'뿐만 아니라 어떠한 문자를 사용해도 상관없으며, 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있음
  • 타입 변수는 클래스에서뿐만 아니라 메소드의 매개변수나 반환값으로도 사용할 수 있음

 

제네릭의 생성

  • 타입 변수 자리에 사용할 실제 타입을 명시해야 함
    • 타입 변수 자리에 사용할 실제 타입을 명시할 때 기본 타입을 바로 사용할 수는 없음 Integer와 같이 래퍼(wrapper) 클래스를 사용해야 함
    • 내부적으로는 정의된 타입 변수가 명시된 실제 타입으로 변환되어 처리됨
MyArray<Integer> myArr = new MyArray<Integer>();
  • Java 7부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있음
MyArray<Integer> myArr = new MyArray<>();

 

제네릭의 사용

  • 메소드의 인자로는 제네릭을 생성할 때 지정한 데이터타입의 값을 넣어줘야 함
public class Box<E> {
  private E obj;
  public void setObj(E obj){
    this.obj = obj;
  }

  public E getObj(){
    return obj;
  }
}

public class BoxExam {
  public static void main(String[] args) {
    Box<Object> box = new Box<>();
    box.setObj(new Object());
    Object obj = box.getObj();

    Box<String> box2 = new Box<>();
    box2.setObj("hello");
    String str = box2.getObj();
    System.out.println(str);

    Box<Integer> box3 = new Box<>();
    //Integer라고 선언했는데 int 값이 들어가는 이유
    //오토박싱과 오토언박싱이 적용되어
    //자동으로 int로 바꿔서 넣어주고 자동으로 int로 바꿔서 꺼내주기 때문
    box3.setObj(1);
    int value = box3.getObj();
    System.out.println(value);
  }
}
  • 아래 코드에서 Integer라고 선언했는데 int 값이 들어가는 이유
Box<Integer> box3 = new Box<>();
box3.setObj(1);
  • 오토박싱과 오토언박싱이 적용되어 자동으로 int로 바꿔서 넣어주고 자동으로 int로 바꿔서 꺼내주기 때문

 

 Set 

중복이 없고, 순서도 없는 자료구조

  • 구현 클래스: Hashset과 TreeSet

 

  • Set 인터페이스를 구현하는 HashSet을 인스턴스로 만드는 예제
    • Set, HashSet, Iterator를 사용할 때마다 import해야 함(Ctrl+Space로 자동완성해도 됨)
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetExam {
  public static void main(String[] args) {
    //제네릭을 이용해 String만 다루도록 지정
    //Set은 인터페이스라 객체 생성 불가능 > Set을 구현한 클래스인 HashSet으로 객체 생성
    Set<String> set1 = new HashSet<>();
    
    //Set의 메소드 add는 값을 저장할 때마다 boolean값을 반환
    //Set은 중복이 불가능하기 때문에 같은 값을 넣으면 add 메소드는 false를 반환
    boolean flag1 = set1.add("kim");
    boolean flag2 = set1.add("lee");
    boolean flag3 = set1.add("kim");

    //저장된 크기를 출력. 3개를 저장했지만, 이미 같은 값이 있었기 때문에 2개가 출력
    System.out.println(set1.size());
    System.out.println(flag1);  //true
    System.out.println(flag2);  //true
    System.out.println(flag3);  //false
    
    //값을 하나씩 꺼내보기 위해서는* Set의 부모클래스인 Collection이 갖고 있는 Iterator 인터페이스를 이용
    Iterator<String> iter = set1.iterator();

    while (iter.hasNext()) {   // 꺼낼 것이 있다면 true 리턴
      // next()메소드는 하나를 꺼냄. 하나를 꺼내면 자동으로 다음 것을 참조.
      String str = iter.next();
      System.out.println(str);
    }
  }
}
  • 결과
    • 2
      true
      true
      false
      kang
      kim

 

  • Set의 메소드 add는 값을 저장할 때마다 boolean값을 반환
  • Set은 중복이 불가능하기 때문에 같은 값을 넣으면 add 메소드는 false를 반환
boolean flag1 = set1.add("kim");
System.out.println(flag1);  *//true*
  • Set 자료구조에 들어있는 값들을 하나씩 꺼내보기 위해서는 Set의 부모클래스인 Collection이 갖고 있는 Iterator 인터페이스를 이용해야 함
  • Set 인터페이스는 Collection 인터페이스를 상속받는데, Collection 인터페이스의 iterator 메소드도 사용할 수 있게 됨
Iterator<String> iter = set1.iterator();
  • Iterator 인터페이스가 갖고 있는 hasNext( )메소드 활용
    • hasNext( ): 꺼낼 것이 있는지 없는지 살펴보고 있으면 true, 없으면 false 리턴
  • 값을 꺼낼 때 index가 필요하진 않기 때문에 간단하게 while문 사용
  • Iterator 인터페이스가 갖고 있는 hasNext( )메소드 활용
    • next( ): 하나를 꺼내고 나면, 자동으로 다음 것을 참조.
while (iter.hasNext()) {   *// 꺼낼 것이 있다면 true 리턴*
  String str = iter.next();
**  System.out.println(str);
}

 

실습 1

  • 문제 설명
    • 다음 코드는 set에 a를 두 번 더하고, b를 한 번 더합니다. 출력해 보면 a와 b가 각각 한 번씩만 출력되는데요. set은 이미 있는 값이면 값을 더해도 2개가 아니라 하나의 값만 유지하기 때문입니다. [제출]을 눌러서 출력을 확인해 보세요.
    • 참고: set의 내용은 for each문 또는 Iterator를 활용해서 출력할 수 있습니다. for each문을 복습하려면 이 링크를 참고하세요.
  • SetExam 클래스
import java.util.*;

public class SetExam{
  public static void main(String[] args){
    Set<String> set = new HashSet<String>();
    set.add("a");
    set.add("a");
    set.add("b");
        
    System.out.println("set의 내용을 출력합니다.");
    for(String str : set){
      System.out.println(str);
    }
  }
}

 

실습 2

  • 문제 설명
    • Iterator iter의 hasNext와 next메소드를 이용해서 set의 내용을 모두 출력해 보세요.
  • SetExam 클래스
import java.util.*;

public class SetExam{
  public static void main(String[] args){
    Set<String> set = new HashSet<String>();
    set.add("a");
    set.add("b");
        
    Iterator<String> iter = set.iterator();
    //iter를 이용해서 set의 내용을 출력하세요.
    while(iter.hasNext()){
      String str = iter.next();
      System.out.println(str);
    }
  }
}


 

 List 

데이터의 중복이 있을 수 있고, 순서도 있음

  • 구현 클래스: Vector, ArrayList, LinkedList, Stack, Queue

  • 리스트와 배열
    • 공통점: 자료구조임
    • 차이점
      • 리스트: 저장공간이 필요에 따라 자유롭게 늘어남 > 길이를 알 수 없는 배열을 더할 때 유용
      • 배열: 한 번 생성하면 크기 변경 불가
  • List 인터페이스를 구현하는 ArrayList 클래스 예제
import java.util.ArrayList;
import java.util.List;

public class ListExam {
  public static void main(String[] args) {
    List<String> list = new ArrayList<>();

    // list에 3개의 문자열을 저장
    list.add("kim");
    list.add("lee");
    list.add("kim");

    //list에 저장된 자료의 수를 출력 (중복을 허용하므로 3 출력)
    System.out.println(list.size());
    for(int i = 0; i < list.size(); i++){
      String str = list.get(i);
      System.out.println(str);
    }
  }   
}
  • List는 순서가 있는 자료구조이기 때문에 index를 활용하는 for문 사용 가능
  • List에서 값을 꺼낼 땐 get( ) 메소드 사용
    • get(int) 메소드: 0번째, 1번째, …, n번째의 자료를 꺼낼 수 있음
  • 결과
    • 3
      kim
      kang
      kim

 

실습

  • 문제 설명
    • List는 길이가 정해져 있지 않기 때문에 길이를 알 수 없는 배열을 더할때 유용하게 사용할 수 있습니다. 다음 코드의 addArray는 매개변수로 두개의 String배열을 받고 있는데요. arr1과 arr2의 각 값을 순서대로 list에 저장해서 return하도록 만들어 보세요.
  • ListExam 클래스
import java.util.*;

public class ListExam{
  public List<String> addArray(String[]arr1, String[]arr2){

    List<String> list = new ArrayList<String>();
        
    for(String str : arr1){
      System.out.println(list.add(str));     
    }
        
    for(String str : arr2){
      System.out.println(list.add(str));
    }
     
    return list;

  }
    
public static void main(String[] args){
  }
}


 

 Map 

key와 value를 쌍으로 저장하는 자료구조

  • put( ) 메소드: Map에 값을 추가
  • get(Object) 메소드: key를 매개변수로 받아들여 원하는 값을 꺼냄
  • keySet( ) 메소드: 자신이 가지고 있는 모든 Key들에 대한 정보를 읽어들일 수 있는 Set을 반환
    • Set을 모두 꺼내기 위해 Iterator 인터페이스 사용
  • 구현 클래스: HashMap, TreeMap, Hashtable, Properties

key는 중복될 수 없고, value은 중복될 수 있음

 

  • Map 인터페이스를 구현하는 HashMap 클래스 예제
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;   
public class MapExam {  
  public static void main(String[] args) {
    //Key, Value가 모두 String 타입인 HashMap인스턴스를 만듦
    Map<String, String> map = new HashMap<>();

    //key와 value값을 put으로 저장
    map.put("001", "kim");
    map.put("002", "lee");
    map.put("003", "choi");
    //같은 key가 2개 존재할 수 없음. 첫 번째로 저장했던 001, kim은 001, kang으로 바뀜
    map.put("001", "kang");

    //map에 저장된 자료의 수를 출력 - 3이 출력됨
    System.out.println(map.size());

    //키가 001, 002, 003인 값을 꺼내 출력
    System.out.println(map.get("001"));
    System.out.println(map.get("002"));
    System.out.println(map.get("003"));

    // map에 저장된 모든 key들을 Set자료구조로 꺼냄
    Set<String> keys = map.keySet();
    // Set자료구조에 있는 모든 key를 꺼내기 위하여 Iterator를 구함
    Iterator<String> iter = keys.iterator();
    while (iter.hasNext()) {
      // key를 꺼냄
      String key = iter.next();
      //key에 해당하는 value를 꺼냄
      String value = map.get(key);
      //key와 value를 출력
      System.out.println(key + " : " + value);
    }
  }
}
  • Map 인터페이스에서 key 값은 중복될 수 없음
  • 나중에 들어온 값이 원래 있던 값을 대체함
  • Map에 값을 추가하기 위해서는 put( ) 메소드 사용
  • 원하는 값을 꺼내기 위해서는 key를 매개변수로 받아들이는 get(Object) 메소드 사용
map.put("001", "kim");
map.put("002", "lee");
map.put("003", "choi");
**map.put("001", "kang");

System.out.println(map.size());     //3
System.out.println(map.get("001")); //kang
  • keySet( ) 메소드: 자신이 가지고 있는 모든 Key들에 대한 정보를 읽어들일 수 있는 Set을 반환
Set<String> keys = map.keySet();
  • Set을 모두 꺼낼 때 사용하는 Iterator 인터페이스 사용
Iterator<String> iter = keys.iterator();
  while (iter.hasNext()) {
  *// key를 꺼냄*
  String key = iter.next();
  *//key에 해당하는 value를 꺼냄*
  String value = map.get(key);
  *//key와 value를 출력*
  System.out.println(key + " : " + value);
    }

 

실습

  • 문제 설명
    • products는 상품의 이름을 key로 가격을 값으로 가지는 Map입니다. "가위"는 2500원, "크레파스"는 5000원이 되도록 products에 값을 추가해 보세요.
  • MapExam 클래스
import java.util.*;

public class MapExam{
  public Map<String, Integer> makeMap(){
    Map<String, Integer> products = new HashMap<>();
    //상품의 이름과 값을 products에 추가해 보세요.
    products.put("가위", 2500);
    products.put("크레파스", 5000);
        
    return products;
  }
    
  public static void main(String[] args){
  }
}