프로그래밍언어 자바 Part-1

6. 자료구조와 컬렉션프레임워크

이번 강좌에서는 자료구조의 주요 개념과 자바 컬렉션프레임워크를 배우고 List, Map, Set 계열의 데이터들을 프로그램내에서 다루기 위한 방법을 배웁니다.

이 강의를 통해 자료구조가 무엇인지 이해하고 자바 프로그램 구현에 컬렉션프레임워크를 활용할 수 있게 됩니다.



01: 자료구조(Data Structure)

자료구조란?

프로그램을 작성하는 과정에는 여러 데이터가 필요 합니다. 예를들어 100명의 학생 성적을 처리한다고 할때 각각의 성적 값을 변수에 할당한다면 100개의 변수가 필요합니다. 이런경우 배열을 이용하면 하나의 변수명으로 100개의 데이터를 처리할 수 있게 됩니다.

자료구조는 컴퓨터 프로그램에서 데이터를 처리하기 위해 만든 구조로 Array, List, Map이 대표적인 형태 입니다. 이 외에 프로그램 언어에 따라 Tuple, Dictionary 등을 사용하기도 합니다.

배열(Array)

배열은 가장 전통적이고 기본이 되는 자료 구조 입니다. 데이터를 순차적으로 저장해 0부터 시작하는 인덱스를 통해 접근할 수 있습니다.

자바 역시 배열은 가장 기본적인 자료형으로 프로그램 개발에 빼놓을수 없는 필수 요소 입니다. 그러나 구조에 따른 제약과 사용의 불편함등으로 인해 List를 더 많이 사용합니다. 그러나 순차적으로 사용하는 단순한 숫자나 문자등으로 이루어진 집합형 데이터의 처리에 있어 배열만큼 간단하고 빠른 자료구조는 없습니다.

리스트(List)

배열과 유사한 순차적인 자료구조를 제공 합니다. 객체지향 프로그램언어에는 보통 List 자료구조가 기본적으로 제공되며 그렇지 않을 경우 직접 자료구조를 구현하거나 구현된 라이브러리를 사용해야 합니다.

데이터 접근을 위해 인덱스를 사용해야 하는 점은 배열과 같지만 배열의 모든 문제점을 해결하고 있습니다.

Linked List는 현재 데이터에 다음 데이터를 읽을 수 있는 정보를 추가한 것으로 불연속적으로 존재하는 데이터를 서로 연결할 수 있는 방법을 제공 합니다. Double Linked List는 이전과 다음 데이터 정보를 모두 가지고 있는 형태이며 자바의 경우 LinkedList 클래스가 제공되는데 실제로는 Double Circular Linked List(순환구조가 추가된 Double Linked List) 형태를 구현해 둔 것입니다.

맵(Map)

데이터를 Key:Value(키:값)의 쌍으로 저장하는 방식입니다. 실제 데이터가 저장되는 형태는 내부구조에 따르며 사용하는 쪽에서는 내부구조에 대해 신경쓸 필요가 없는 형태 입니다.

맵을 사용했을때 얻을 수 있는 가장 큰 장점은 원하는 데이터를 손쉽게 찾을 수 있다는 점입니다.

예를 들면 학생들의 성적을 관리하는 자료구조를 만들때 학생별 성적을 List 로 만들고 학번을 Key 로 하는 Map 을 만들어 List 를 저장하면 학범을 통해 손쉽게 성적관리가 가능합니다.

이터레이터(Iterator)

이터레이터는 서로다른 자료구조(Vector, ArrayList, LinkedList)의 데이터를 동일한 방법으로 다음 데이터에 접근하는 방법을 제공하는 인터페이스로 자바 컬렉션 프레임워크의 일부 입니다.


02: 컬렉션 프레임워크(Collection Framework)

컬렉션 프레임워크는 자바에서 데이터를 저장하는 클래스들을 표준화한 설계 구조를 말합니다. 이러한 구조를 바탕으로 자바의 기본 자료구조 클래스들이 구성되어 있으며 체계화되고 일관된 구조를 가지게 되었습니다.

Collection, Map, List, Set 인터페이스를 중심으로 다음과 같은 클래스 계층구조를 형성하고 있습니다.

[그림: 자바 컬렉션 프레임워크 구조]

다음은 주요 인터페이스에 대한 설명 입니다.

인터페이스 설 명 특 징 대표 구현 클래스
List 순서가 있는 데이터의 집합. 데이터의 중복을 허용 ArrayList, LinkedList
Set 순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않음 HashSet, LinkedHashSet
Map 키(key)와 값(value)의 쌍으로 이루어진 데이터 집합. 순서 유지 X, 키 중복 X, 값 중복 O HashMap, LinkedHashMap, Properties

Collection 인터페이스

List 와 Set 의 상위 인터페이스 입니다. 즉, List 와 Set 를 구현한 모든 클래스들은 Collection 인터페이스의 메서드를 사용할 수 있으므로 구현클래스와 상관없이 동일한 방법으로 데이터를 다룰 수 있습니다.

컬렉션안에 들어가는 데이터들은 모든 타입이 가능하지만 타입이 다른 경우 일관된 처리가 어렵기 때문에 타입 파라미터를 사용합니다.

Collection<String> c = new HashSet<>();

컬렉션 객체의 생성

모든 컬렉션 클래스들은 java.util 패키지에 들어 있습니다. 컬렉션 객체를 생성하는 방법은 참조변수 선언시 상위 인터페이스 타입을 사용하고 객체 생성은 필요에 따라 구체적인 클래스들을 사용하는 형식 입니다.

ArrayList<String> list = new ArrayList<>();     // 권장안됨
List<String> list = new ArrayList<>();  // 권장됨

컬렉션 객체를 생성하는 방법은 다양하며 상황에 따라 적절한 방법을 사용하면 됩니다.

Collection<String> c1 = new HashSet<>();
Collection<String> c2 = Arrays.asList("three", "four"); 
Collection<String> c3 = Collections.singleton("five");

컬렉션에 데이터 추가, 삭제

데이터 추가는 add(), addAll() 메서드를 사용 합니다. 지정된 타입의 데이터를 하나씩 추가할때는 add(), 컬렉션 타입을 추가할때는 addAll() 을 이용해 개별 원소를 꺼내어 추가하는 형식입니다.

c1.add("one");
c1.addAll(c2);

데이터의 삭제는 remove(), removeAll(), retainAll(), clear() 등의 메서드를 사용 합니다. retailAll()의 경우 인자의 데이터를 제외한 나머지 데이터를 모두 삭제하는 메서드이고 clear() 는 모든 데이터를 삭제 합니다.

c1.remove("one");
c1.remove("c2");
c1.retainAll("four");
c1.clear();

컬렉션 데이터 확인 및 변환

특정 데이터가 컬렉션 안에 존재 하는지 혹은 비어 있는지를 확인할 수 있으며 크기를 구할 수 있습니다.

c1.isEmpty();
c1.contains("zero");
c1.containsAll(c2);

컬렉션 데이터의 크기는 size() 메서드를 통해 구할 수 있으며 데이터 변환은 toArray() 메서드를 이용해 배열형태로 변경할 수 있습니다. 다만 Object 타입으로 리턴 되므로 특정 타입을 원한다면 해당 객체 생성 코드를 넣어주어야 합니다.

int size = c1.size();
Object[] converted1 = c1.toArray();
String[] converted2 = c1.toArray(new String[c1.size()]);

컬렉션 데이터의 사용

컬렉션에 들어있는 데이터를 사용하기 위해서는 다소 복잡한 과정을 거쳐야 합니다. 컬렉션 자체는 구체적인 구현이 아니므로 직접적으로 데이터에 접근하는 방법은 포함되어 있지 않습니다.

List 나 Set 인터페이스를 이용해 처리하거나 Collection 인터페이스 타입으로 처리하려면 다음의 방법중 하나를 사용할 수 있습니다.

a) 배열로 변환해서 for문과 사용

특정 위치의 데이터를 직접 선택할 필요없이 순차적으로 모든 데이터를 사용하는 경우 단순하게 for 문만 사용해도 됩니다.

for(String s : c1) {
    System.out.println(s);			
}

그러나 특정 위치의 데이터를 직접 다루고자 한다면 앞에서 다룬 toArray() 메서드를 이용해 배열로 변환해 사용하는 방법을 사용해야 합니다. 배열 데이터는 인덱스로 접근할 수 있으므로 각각의 데이터를 차례로 접근하거나 특정 위치 데이터를 직접 접근해 사용할 수 있습니다.

String[] converted2 = c1.toArray(new String[c1.size()]);
for(int i=0; i < converted2.length; i++) {
    System.out.println(converted2[i]);
}

b) Iterator를 사용

Iterator는 다음 데이터에 접근하는 방법을 제공하는 인터페이스 입니다. Collection 인터페이스는 Iterable 인터페이스를 상속받고 있으며 iterator() 메서드로 Iterator 객체를 구할 수 있습니다.

여기서는 모두 데이터 출력을 예시로 살펴보고 있지만 획득한 데이터를 조작하거나 다른 메서드의 인자로 전달하는등 필요한 작업에 활용할 수 있습니다.

Iterator iter = c1.iterator();
while(iter.hasNext()) {
    System.out.println(iter.next());
}

c) forEach()를 사용

마지막으로 forEach() 메서드를 사용하는 방법입니다. 9장에 배우게될 함수형 프로그래밍과 람다에서 더 자세한 활용을 살펴볼 수 있습니다.

c1.forEach(s -> System.out.println(s));
c1.forEach(System.out::println);

» 실습: Collection 종합 실습예제

실습개요

Collection 인터페이스 활용을 위한 종합 실습 예제 입니다.

소스코드

public class CollectionTest {
    public static void main(String[] args) {
        // Create a new HashSet and add data
        Collection<String> c1 = new HashSet<>();
        c1.add("one");
        c1.add("two");

        // Create a new List with two values
        Collection<String> c2 = Arrays.asList("three", "four");
        Collection<String> c3 = Collections.singleton("five");

        // add all data in c2, c3 to c1
        c1.addAll(c2);
        c1.addAll(c3);

        // get size of collection
        System.out.println("Size of c1 : " + c1.size());

        // convert collection to array
        Object[] converted1 = c1.toArray();
        String[] converted2 = c1.toArray(new String[c1.size()]);

        // print all data in collection using for loop
        for (String s : c1) {
            System.out.println(s);
        }

        // access and print all data in the array which converted from collection
        for (int i = 0; i < converted2.length; i++) {
            System.out.println(converted2[i]);
        }

        // get Iterator object from collection and access the data
        Iterator iter = c1.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }

        // run specific code using every elements in the collection
        c1.forEach(System.out::println);
    }
}

실행결과

Size of c1 : 5
four
one
two
three
five
four
...


03: List, Set

List 와 Set 은 모두 Collection 인터페이스를 상속받고 있으며 List 의 구현 클래스들은 AbstractList 클래스를 상속 받는 구조 입니다.

리스트(List)

List 인터페이스는 배열과 유사한 자료구조로 중복이 허용되면서 저장순서가 유지되는 구조를 제공 합니다. 구현 클래스로는 Vector, ArrayList, LinkedList 가 있으며 가장 널리 사용되는것은 ArrayList 입니다.

주요 메서드는 다음과 같습니다. 각 메서드의 구체적인 인자나 사용법은 API문서와 예제를 참고하도록 합니다.

메서드 설명
add(), addAll() 새로운 요소를 추가, 위치를 지정하거나 컬렉션 데이터를 한번에 추가하는 것이 가능
get() 지정된 위치(index)에 있는 객체 반환
indexOf() 객체의 위치(index) 반환
lastIndexOf() List의 마지막 요소부터 역방향으로 위치 반환
listIterator() List의 객체에 접근할 수 있는 ListIterator를 반환
remove() 지정된 위치에 있는 객체를 삭제하고 삭제된 객체를 반환
set() 지정된 위치에 객체를 저장
sort() 지정된 Comparator로 List 요소 정렬
subList() 지정된 범위에 있는 객체를 새로운 List로 반환


ArrayList, LinkedList

가장 대표적인 List 구현 클래스 입니다. 일반적으로 모든 용도에서 사용할 수 있으나 데이터 추가나 삭제가 잦은 경우 처리속도에 문제가 있을 수 있습니다.

ArrayList 는 내부적으로 배열 구조를 사용하고 있으므로 중간에 데이터가 추가되거나 삭제될 경우 기존 데이터를 복사해 새로운 구조를 만들어야 하기 때문으로 이와 같은 경우에는 LinkedList 를 사용할 것을 권장 합니다.

기본 사용법

Collection 과 마찬가지로 타입 선언은 인터페이스 타입으로 하고 객체 생성은 ArrayList 나 LinkedList 를 사용하는 형태 입니다.

List<String> l1 = new ArrayList<>();
List<String> l2 = Arrays.asList("one", "two"); 
List<String> l3 = List.of("three", "four");

l1.add("five");
l1.allAll(l2);
l1.set(0,"zero");

System.out.println(l1.get(0));

LinkedList 의 경우 생성 클래스만 다르고 사용하는 방법은 동일 하며 내부적으로 요소의 추가와 삭제등을 처리하는 방법에 차이가 있습니다.

LinkedList<String> llist = new LinkedList<>();
llist.addAll(l2);
llist.addAll(1,l3);
llist.add("five");

정렬

원소들의 정렬을 위해서는 다양한 방법이 사용될 수 있습니다. 기본적으로는 Collections.sort(List<T> list)를 사용할 수 있습니다. 이 경우 내림차순으로 정렬이 이루어지며 원래 List 객체는 원소들이 정렬된 결과로 바뀌게 됩니다. 이 방법으로는 올림차순 정렬은 할 수 없고 Comparator 를 추가로 인자로 작성해 주어야 가능합니다.

Collections.sort(l1);
System.out.println(l1);

또다른 방법으로는 List 의 sort(Comparator<? super E> c) 메서드를 이용하는 것입니다. 이 경우 정렬 방법을 다루는 Comparator 객체가 필요하게 됩니다. 별도 클래스로 만든 요소 타입을 사용하고 있다면 해당 클래스에 Comparable 인터페이스를 구현하고 compareTo() 메서드를 구현해 주면 되고 그렇지 않다면 sort() 메서드의 인자 부분에 직접 내부 익명 클래스로 구현하는 방법을 사용할 수 있습니다.

l1.sort(new Comparator<Object>() {
    @Override
    public int compare(Object o1, Object o2) {
        return o2.toString().compareTo(o1.toString());
    }
});

마지막으로 stream api 를 이용해 구현하는 방법으로 자세한 내용은 10장에서 다시 살펴보게 됩니다.

l1.stream().sorted((o1, o2) -> o2.toString().compareTo(o1.toString())).forEach(System.out::println);

» 실습: ArrayList, LinkedList 생성과 데이터 다루기 실습 예제

실습개요

ArrayList, LinkedList 객체를 생성하고 다양한 메서드를 사용해 데이터를 다루거나 정렬하는 방법을 살펴 봅니다.

소스코드

public class ListTest {
    public static void main(String[] args) {
        // create new List
        List<String> l1 = new ArrayList<>();
        List<String> l2 = Arrays.asList("one", "two");
        List<String> l3 = List.of("three", "four");

        // add data to List
        l1.addAll(l2);
        l1.addAll(1, l3);
        l1.add("five");

        System.out.println("## element in List");
        System.out.println(l1);

        // create new LinkedList and add data
        LinkedList<String> llist = new LinkedList<>();
        llist.addAll(l2);
        llist.addAll(1, l3);
        llist.add("five");

        System.out.println("## element in LinkedList");
        System.out.println(l1);

        // access data with index
        System.out.println("## first element: " + l1.get(0));
        System.out.println("## last index of three: " + l1.lastIndexOf("three"));

        // change data in List
        l1.set(0, "zero");
        System.out.println("## after set(): element in LinkedList");
        System.out.println(l1);

        // Descending sort
        Collections.sort(l1);
        System.out.println("## Descending sort of l1");
        System.out.println(l1);

        // Ascending sort
        l1.sort(new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                return o2.toString().compareTo(o1.toString());
            }

        });
        System.out.println("## Ascending sort of l1");
        System.out.println(l1);

        // Ascending sort with stream api
        System.out.println("## Ascending sort with stream api");
        l1.stream().sorted((o1, o2) -> o2.toString().compareTo(o1.toString())).forEach(System.out::println);
    }
}

실행결과

## element in List
[one, three, four, two, five]
## element in LinkedList
[one, three, four, two, five]
## first element: one
## last index of three: 1
## after set(): element in LinkedList
[zero, three, four, two, five]
## Descending sort of l1
[five, four, three, two, zero]
## Ascending sort of l1
[zero, two, three, four, five]
## Ascending sort with stream api
zero
two
three
four
five

Set

Set 인터페이스는 List와 유사하지만 중복이 허용되지 않고 기본적으로는 순서가 유지 되지 않습니다. 구현 클래스로는 HashSet, LinkedHashSet, EnumSet, TreeSet, CopyOnWriteArraySet 등이 있으며 가장 널리 사용되는것은 HashSet 입니다. 순서가 필요한 경우 LinkedHashSet 클래스나 SortedSet 인터페이스를 구현한 TreeSet등을 사용할 수 있습니다.

주요 메서드는 다음과 같습니다. 각 메서드의 구체적인 인자나 사용법은 API문서와 예제를 참고하도록 합니다.

메서드 설명
add(), addAll() 기존에 없는 새로운 요소를 추가, 컬렉션 데이터를 한번에 추가하는 것도 가능
clear() 모든 요소를 삭제
contains(), containsAll() 인자의 객체를 포함하고 있는지 확인, 컬렉션 전체를 비교할수도 있음
isEmpty 요소가 하나도 없는지 확인
iterator() 현재 Set의 객체에 접근할 수 있는 Iterator를 반환
remove(), removeAll() 특정 객체를 삭제하거나 컬렉션 전체를 삭제
size() Set 에 저장된 요소의 크기를 반환
toArray() 현재 Set의 요소를 배열로 반환


HashSet, LinkedHashSet

가장 대표적인 Set 구현 클래스 입니다. 데이터가 중복되지 않는 자료구조가 필요한 경우 사용할 수 있으며 LinkedHashSet 의 경우 데이터가 입력된 순서로 저장됩니다.

기본 사용법

Collection 과 마찬가지로 타입 선언은 인터페이스 타입으로 하고 객체 생성은 HashSet 이나 LinkedHashSet을 사용하는 형태 입니다.

Set<String> s1 = new HashSet<>();
Set<String> s2 = Set.of("three","four");

s1.addAll(Arrays.asList("one","two"));
s1.addAll(s2);
s1.add("five");
s1.add("two"); // 기존에 있으므로 새로 추가 안됨
s1.remove("five");
boolean check = s1.contains("one");

LinkedHashSet 의 경우 생성 클래스만 다르고 사용하는 방법은 기본적으로 동일 합니다.

LinkedHashSet<String> lset = new LinkedHashSet<>();
lset.addAll(Arrays.asList("one","two","three","four"));
lset.add("five");

Iterator 가 필요한 경우 다음과 같이 Iterator 객체를 가지고와 사용할 수 있습니다.

Iterator<String> iter = lset.iterator();
while(iter.hasNext()) {
    System.out.println(iter.next());
}

TreeSet

SortedSet 인터페이스를 구현한 클래스 입니다. HashSet과 동일하게 중복된 데이터를 저장할 수 없으며 LinkedHashSet이 입력한 순서로 저장되는것과 달리 아니라 오름차순으로 데이터를 정렬 합니다.

Set<Integer> tset = new TreeSet<>();
tset.addAll(Arrays.asList(50,10,60,20));

만일 오름차순으로 정렬하기를 원한다면 다음과 같이 스트림 api 를 사용할 수 있습니다.

tset.stream().sorted((o1, o2) -> o2.toString().compareTo(o1.toString())).forEach(System.out::println);

» 실습: 다양한 Set 객체 생성과 데이터 다루기 실습 예제

실습개요

HashSet, LinkedHashet, TreeSet 객체를 생성하고 다양한 메서드를 사용해 데이터를 다루거나 정렬하는 방법을 살펴 봅니다.

소스코드

public class SetTest {
    public static void main(String[] args) {
        // create new Set
        Set<String> s1 = new HashSet<>();
        Set<String> s2 = Set.of("three", "four");

        // add element to Set
        s1.addAll(Arrays.asList("one", "two"));
        s1.addAll(s2);
        s1.add("five");
        s1.add("two");
        s1.remove("five");

        System.out.println("## element in Set");
        System.out.println(s1);

        // print all elements using stream api
        s1.stream().forEach(System.out::println);

        System.out.println("## check exist element in Set");
        System.out.println(s1.contains("one"));

        // create new LinkedHashSet and add elements
        Set<String> lset = new LinkedHashSet<>();
        lset.addAll(Arrays.asList("one", "two", "three", "four"));
        lset.add("five");

        System.out.println("\n## element in LinkedHashSet");
        System.out.println(lset);

        // get Iterator from LinkedHashSet
        System.out.println("## print element using Iterator");
        Iterator<String> iter = lset.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }

        // create new TreeSet and add elements
        Set<Integer> tset = new TreeSet<>();
        tset.addAll(Arrays.asList(50, 10, 60, 20));

        System.out.println("\n## elements in TreeSet");
        System.out.println(tset);

        // Descending sort with stream api
        System.out.println("## Descending sort with stream api");
        tset.stream().sorted((o1, o2) -> o2.toString().compareTo(o1.toString())).forEach(System.out::println);
    }
}

실행결과

## element in Set
[four, one, two, three]
four
one
two
three
## check exist element in Set
true

## element in LinkedHashSet
[one, two, three, four, five]
## print element using Iterator
one
two
three
four
five

## elements in TreeSet
[10, 20, 50, 60]
## Descending sort with stream api
60
50
20
10


04: Map

Map 은 List 계열과 달리 순차적으로 데이터를 관리하지 않고 Key 와 Value 의 쌍으로 데이터를 관리합니다.

Map 개요

Map은 Collection 인터페이스를 상속받지 않으며 그 자체로 인터페이스로 여러 Map 구현클래스를 가지고 있습니다. 가장 대표적인 클래스는 HashMap 입니다.

기본적인 Map 사용방식은 다음과 같습니다.

Map<String,String> map = new HashMap<>();
map.put("109875","홍길동");
map.put("109894","김사랑");
System.out.println(map.get("109894")); // 김사랑

다음과 같은 주요 메서드를 사용해 데이터를 추가, 검색, 삭제등의 작업을 수행할 수 있으며 특히 전체 데이터를 출력하거나 하는 경우 key, value 의 값을 각각 Set 과 Collection 타입으로 변환하는 메서드가 유용하게 사용 됩니다.

메서드 설명
put(), putAll() 새로운 요소를 추가, 데이터를 한번에 추가하는 것도 가능
get() 특정 Key 에 해당하는 값을 가지고 옴
remove Map 요소 삭제
entrySet key 와 value 값 모두를 Entry 객체로 반환
keySet key 요소만 Set 객체로 반환
size 크기를 반환
values value 요소만 Collection 타입으로 반환

» 실습: Map 기본 예제

실습개요

HashMap 을 이용해 Map 을 생성하고 데이터를 다루는 종합 예제 입니다. 특히 다양한 방법으로 Map 원소의 Key와 Value 를 사용하는 방법을 익히도록 합니다.

소스코드

public class MapTest {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("1922876", "Apple Iphone");
        map.put("1922877", "Apple Ipad");
        map.put("2136861", "Samsung Galaxy");
        map.put("2136863", "Samsung Tablet");

        System.out.println("1922877: " + map.get("1922877"));

        System.out.println("--------------------");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.printf("%s:%s\n", entry.getKey(), entry.getValue());
        }

        System.out.println("--------------------");
        for (String s : map.keySet()) {
            System.out.printf("%s\n", s);
        }

        System.out.println("--------------------");
        for (String s : map.values()) {
            System.out.printf("%s\n", s);
        }
    }
}

» 실습: Map과 List 를 함께 사용한 예제

실습개요

데이터 표현을 위해 Map과 List 는 종종 함께 사용되며 특히 List 의 원소로 객체 타입을 사용하는 경우 보다 복잡한 데이터 표현이 가능합니다. 여기서는 성적목록을 가지는 List와 학생 이름을 키로 하는 Map 구조를 활용한 예제를 살펴 봅니다.

소스코드

public class MapListTest {
    public static void main(String[] args) {
        List<Integer> s1 = Arrays.asList(95, 89, 93, 87, 94);
        List<Integer> s2 = Arrays.asList(99, 79, 91, 89, 91);
        List<Integer> s3 = Arrays.asList(93, 81, 95, 88, 99);

        Map<String, List<Integer>> student = new HashMap<>();
        student.put("홍길동", s1);
        student.put("김사랑", s2);
        student.put("아무개", s3);

        Scanner scan = new Scanner(System.in);
        System.out.print("## 성적 조회할 이름을 입력하세요: ");
        String input = scan.next();

        int total = 0;
        for (int s : student.get(input)) {
            total += s;
        }

        System.out.printf("총점: %d, 평균: %d", total, total / 5);
    }
}

참고 자료