728x90

Wrapper 클래스 : 8개의 기본형을 객체로 다뤄야 할 때 사용하는 클래스

Wrapper classes provide a way to use primitive data types (int, boolean, etc..) as objects.

 

wrapper의 사전적 의미는 '(특히 식품) 포장지'라는 뜻이다.
기본 자료형에 대해서 객체로서 인식되도록 '포장'했다는 의미이다.

 

기본형 타입 변수는 소문자로 시작되는데 Wrapper 클래스 변수는 대문자로 시작된다.

이중에서 Integer 와 Character 래퍼 클래스는 기본형과 형태가 다르고 나머지는 첫글자 대/소문자만 다르다.

 

ArrayList<int> myNumbers = new ArrayList<int>(); // Invalid
ArrayList<Integer> myNumbers = new ArrayList<Integer>(); // Valid

 

자바 1.5 버전 이상부터는 박싱/언박싱이 자동으로 되므로 크게 신경쓸 필요는 없다.

 

사용하는 이유

1. 기본 데이터 타입을 Object로 변환할 수 있다.

2. java.util 패키지의 클래스는 객체만 처리하므로 Wrapper class는 이 경우에도 도움이 된다.

3. ArrayList 등과 같은 Collection Framework의 데이터 구조는 기본 타입이 아닌 객체만 저장하게 되고, Wrapper class를 사용하여 자동박싱/언박싱이 일어난다.
4. 멀티쓰레딩에서 동기화를 지원하려면 객체가 필요하다.

 

예제

import java.util.ArrayList;
 
public class WrapperClass {
    public static void main(String[] args) {
        // Wrapper 클래스 : 8개의 기본형을 객체로 다뤄야 할 때 사용하는 클래스
        // JDK 1.5 이전에는 기본형과 참조형간 연산이 불가능
        // Autoboxing은 자바 컴파일러가 primitive data type을
        // 그에 상응하는 wrapper class 로 자동 변환시켜주는 것을 의미
        // Unboxing 은 자바 컴파일러가 wrapper class 를 primitive data type으로 자동 변환시켜 주는 것
 
        Integer num = 17// Auto Boxing : Integer num = new Integer(17);
        // Boxing : 기본 타입의 데이터를 wrapper class의 인스턴스로 변환하는 과정 Integer num = new Integer(1);
        // wrapper class 인스턴스에 저장된 값을 변경할 수 없다.(산술 연산을 위한 클래스가 아니기 때문) ???
        int n = num; // Unboxing : int n = num.intValue();
        // 언박싱(UnBoxing) : wrapper class의 인스턴스에 저장된 값을 -> 기본 타입의 데이터로 꺼내는 과정
        // JDK 1.5부터 boxing,unboxing이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해준다.
        System.out.println(n);
 
        // Wrapper Class 비교연산 (==, equals)
        Integer num1  = new Integer(100); // 0X100 주소에 100이 저장되어 있다고 하자.
        Integer num2 = new Integer(100); // 0X200 주소에 100이 저장되어 있다고 하자.
        int i = 100//기본타입
 
        System.out.println("num1==num2 ? "+(num1==num2)); // 참조주소 비교 (false)
        System.out.println("num1.equals(num2) ? "+num1.equals(num2)); // 내용(값) 비교 (true)
        System.out.println("num1.compareTo(num2)="+num1.compareTo(num2)); // 같으면 0,우측이 작으면 양수
        System.out.println("num1.toString()="+num1.toString()); // 문자열
        // 래퍼 클래스와 기본자료형과의 비교는 == 연산과 equals연산 모두 가능하다.
        // 컴파일러가 자동으로 오토박싱과 언박싱을 해주기 때문이다.
        System.out.println("래퍼클래스 == 기본타입 : "+(num1 == i)); // true
        System.out.println("래퍼클래스.equals(기본타입) : "+(num1.equals(i))); // true
 
        System.out.println("-----------------------");
        // 래퍼 객체에 상응하는 값을 얻기 위해 사용
        // intValue(), byteValue(), shortValue(), longValue(),
        // floatValue(), doubleValue(), charValue(), booleanValue().
        Integer myInt = 5;
        Double myDouble = 5.99;
        Character myChar = 'A';
        System.out.println(myInt.intValue());
        System.out.println(myDouble.doubleValue());
        System.out.println(myChar.charValue());
 
        System.out.println("-----------------------");
        // 문자열을 숫자로 변환하기
        int        i1  = new Integer("100").intValue();
        int        i3 = Integer.parseInt("100");
        Integer  i4 = Integer.valueOf("100");
 
        int i5 = Integer.parseInt("100",2);
        int i6 = Integer.parseInt("100",8);
        int i7 = Integer.parseInt("100",16);
        int i8 = Integer.parseInt("FF"16);
 
        System.out.println(Integer.parseInt("100"2));
        System.out.println(Integer.parseInt("100"8));
        System.out.println(Integer.parseInt("100"10));
        System.out.println(Integer.parseInt("100"16));
        System.out.println(Integer.parseInt("FF"16));
 
        System.out.println("-----------------------");
        //ArrayList<int> myNumbers = new ArrayList<int>(); // Invalid
        // Generics 타입에 primitive data types는 사용 불가.
        ArrayList<Integer> list = new ArrayList<>();
        list.add(new Integer(100)); // 리스트에는 객체만 추가 가능
        list.add(100); // JDK 1.5 이전에는 에러. Autoboxing
 
        Integer ii = list.get(0); // list 에 저장된 첫번째 객체를 꺼낸다.
        int jj = list.get(0); // intValue()로 Integer를 int로 변환
 
        System.out.println(list.getClass().getName()); // 객체 Type 확인
        System.out.println(ii.getClass().getName()); // 객체 Type 확인
        System.out.println(list.get(0));
    }
}
 

 

'안드로이드 > Java 문법' 카테고리의 다른 글

[Java] Stream 예제 2  (0) 2021.11.16
[Java ] Stream 예제  (0) 2021.11.15
[Java] Generics 예제1  (2) 2021.11.10
HashMap 예제  (0) 2021.10.30
[Java] HashSet 를 이용한 ArrayList 중복제거 및 정렬  (0) 2020.12.12
블로그 이미지

Link2Me

,
728x90

이 내용은 다른 블로그에 있는 걸 거의 그대로 베껴서 코드가 동작하는 걸 확인해보고 적어둔다.

문제

//member.csv 파일 일부 
이름, 취미, 소개 
김프로, 축구:농구:야구, 구기종목 좋아요 
정프로, 개발:당구:족구, 개발하는데 뛰긴 싫어 
앙몬드, 피아노, 죠르디가 좋아요 좋아좋아너무좋아 
죠르디, 스포츠댄스:개발, 개발하는 죠르디
정미애, 노래:스포츠댄스:개발, 이선희 모창가수 좋아요
 

1. CSV 파일을 읽어서 취미별 인원수를 구하라.

2. CSV 파일을 읽어서 취미별 정씨성을 가진 멤버 수를 구하라.

3. CSV 파일을 읽어서 소개 내용에 '좋아'가 몇번 등장하는 지 카운트하라.

 

 

코드 구현 예제

import java.io.*;
import java.util.*;
 
public class StreamEX6 {
    public static void main(String[] args) {
        // 코드 출처 : https://jeong-pro.tistory.com/212 (동작하는 코드로 작성)
        StreamEX6 st = new StreamEX6();
        st.printMemberCountGroupByHobby();
        System.out.println("======================");
        st.printMemberCountGroupByJeongMember();
        System.out.println("======================");
        st.printLikeCount();
    }
 
    public void printMemberCountGroupByHobby() { // 취미별 인원수 구하기
        List<List<String>> members = readCSV();
 
        members.remove(0); //첫줄 제거
        //System.out.println(members); // 출력하여 확인
 
        Map<String, Integer> result = new HashMap<>(); //결과를 담을 해시맵 구성
        members.stream()
                .flatMap(line -> Arrays.stream(line.get(1).replace(" ","").split(":")))
                //.peek(s->System.out.printf("hobby=%s%n",s))
                .forEach(hobby -> result.merge(hobby, 1, (oldValue, newValue)->++oldValue));
        //출력
        result.entrySet().forEach(entry -> System.out.println(entry.getKey() + " " + entry.getValue()));
    }
 
    public void printMemberCountGroupByJeongMember(){ // 취미별 정씨 성을 갖는 멤버 수
        List<List<String>> members = readCSV();
 
        members.remove(0); //첫줄 제거
        //System.out.println(members);
 
        Map<String, Integer> result = new HashMap<>(); //결과를 담을 해시맵 구성
        members.stream()
                .filter(line->line.get(0).startsWith("정"))
                .flatMap(line -> Arrays.stream(line.get(1).replace(" ","").split(":")))
                //.peek(s->System.out.printf("hobby=%s%n",s))
                .forEach(hobby -> result.merge(hobby, 1, (oldValue, newValue)->++oldValue));
        //출력
        result.entrySet().forEach(entry -> System.out.println(entry.getKey() + " " + entry.getValue()));
    }
 
    public void printLikeCount(){ // 소개 내용에 '좋아'가 몇번 등장하는지 구하기
        List<List<String>> members = readCSV();
 
        members.remove(0); // 첫줄 제거
        //System.out.println(members);
 
        final String word = "좋아";
        int result = members.stream()
                .map(line -> countFindString(line.get(2),word))
                .reduce(0,Integer::sum);
 
        // 출력
        System.out.println(word + " " + result);
    }
 
    private int countFindString(String source, String target){
        int idx = source.indexOf(target);
        if(idx == -1){
            return 0;
        } else {
            return 1 + countFindString(source.substring(idx+1), target);
        }
    }
 
 
    public static List<List<String>> readCSV() {
        List<List<String>> csvList = new ArrayList<List<String>>();
        File csv = new File("c:\\Workspace\\Java\\StreamEX\\src\\members.csv"); // 절대 경로
        BufferedReader reader = null;
        String line = "";
 
        try {
            reader = new BufferedReader(new FileReader(csv));
            while ((line = reader.readLine()) != null) { // readLine()은 파일에서 개행된 한 줄의 데이터를 읽어온다.
                List<String> tmpList = new ArrayList<String>();
                String[] lineArr = line.split(","); // 파일의 한 줄을 ,로 나누어 배열에 저장 후 리스트로 변환한다.
                tmpList = Arrays.asList(lineArr); // 배열을 리스트로 변환
                csvList.add(tmpList);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close(); // 사용 후 BufferedReader를 닫아준다.
                }
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
        //System.out.println(csvList); // 읽어들인 파일의 리스트 확인
        return csvList;
    }
 
}
 

 

 

'안드로이드 > Java 문법' 카테고리의 다른 글

[java] Wrapper Class  (0) 2021.11.18
[Java ] Stream 예제  (0) 2021.11.15
[Java] Generics 예제1  (2) 2021.11.10
HashMap 예제  (0) 2021.10.30
[Java] HashSet 를 이용한 ArrayList 중복제거 및 정렬  (0) 2020.12.12
블로그 이미지

Link2Me

,
728x90

Stream 이란 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것이다.

- 스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다.

- 스트림은 Iterator 처럼 1회용이다. (필요하면 다시 스트림을 생성해야 함)

- 람다식으로 처리할 수 있도록 도와주는 반복자이다. (JDK 1.8 이상)

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
 
public class StreamEX1 {
    public static void main(String[] args) {
        // Arrays.asList : 배열을 리스트로 바꾸기
        List<Integer> list = Arrays.asList(1,2,3,8,9,10,4,5,6,7);
        System.out.println(list);
 
        // Stream : 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
        // ① 스트림 만들기  ② 중간 연산  ③ 최종 연산
        // 스트림은 데이터 소스로부터 데이터를 읽기만 한다. (변경 하지 않음)
        // 스트림은 Iterator처럼 1회용이다.
        Stream<Integer> stream = list.stream(); // list를 stream 으로 변환
        stream.forEach(System.out::print);
 
        System.out.println();
 
        Stream<Integer> stream2 = list.stream();
        stream2.sorted().map(String::valueOf).forEach(i-> System.out.print(i+":"));
        // 원하지 않는 결과를 출력한다.
 
        System.out.println();
 
        final String result = list.stream().
                sorted().
                map(String::valueOf).
                collect(Collectors.joining(":")); // collect와 forEach 동시 사용 불가
        System.out.println(result);
 
        System.out.println();
        System.out.println("********** 로또 번호 **************");
 
        // 로또번호 출력
        // IntStream : 오토박싱 & 언박싱의 비효율이 제거됨.
        // Stream<Integer> 대신에 IntStream 사용
        IntStream intStream = new Random().ints(1,46);
        final String lotto_result = intStream.distinct().limit(6).sorted()
                .mapToObj(String::valueOf).collect(Collectors.joining(","));
        System.out.println(lotto_result);
 
        // ********************************************************************
        System.out.println();
        System.out.println("******* String.valueof ***********");
        // String.valueof
        Map<String,Object> map = new HashMap<>();
 
        map.put("param1""3번");
        map.put("param2"2);
        map.put("param3"null);
 
        System.out.println(String.valueOf(map.get("param1")));
        System.out.println(String.valueOf(map.get("param2")));
        System.out.println(String.valueOf(map.get("param3"))); // null 반환
        System.out.println(map.get("param3")); // null 반환
        //System.out.println(map.get("param3").toString()); // 에러 발생
 
        for(Map.Entry<String,Object> entry : map.entrySet()) {
            System.out.println("Key : " + entry.getKey());
            System.out.println("Value : " + entry.getValue());
        }
    }
}
 

 

결과

1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10

 

 

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
 
public class StreamEX2 {
    public static void main(String[] args) {
        // Arrays.asList : 배열을 리스트로 바꾸기
        List<String> list = Arrays.asList("홍길동""이순신""강감찬");
        System.out.println(list);
 
        // Iterator 이용 (Java 7 이하)
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()) {
            String name = iterator.next();
            System.out.println(name);
        }
 
        System.out.println("********");
 
        // for문 이용
        for (String name : list) {
            System.out.println(name);
        }
 
        System.out.println("********");
 
        // Stream 은 컬렉션(배열포함)의 요소를 하나씩 참조해서 람다식으로 처리하는 반복자
        // Stream 이용(Java 8)
        Stream<String> stream = list.stream();
        stream.forEach(name->System.out.println(name)); // 최종 연산
        // 스트림은 데이터 소스로부터 데이터를 읽기만 한다. (변경 하지 않음)
        // 스트림은 Iterator처럼 1회용이다.
 
    }
}
 
 

 

 

import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class StreamEX3 {
    public static void main(String[] args) {
        ArrayList<Student> arr = new ArrayList<>();
        arr.add(new Student("이순신"3300));
        arr.add(new Student("김재동"1200));
        arr.add(new Student("강감찬"2170));
        arr.add(new Student("소지섭"2190));
        arr.add(new Student("소유진"1200));
        arr.add(new Student("남궁성"3270));
        arr.add(new Student("신용권"3280));
 
        // HashSet(Set인터페이스를 구현한 대표적인 컬렉션 클래스)을 사용하여 중복 제거하기
        Set<Student> arr2 = new HashSet<>(arr);
        ArrayList<Student> resArr2 = new ArrayList<>(arr2);
 
        Collections.sort(resArr2,Comparator.comparing(Student::getBan) // 반별 정렬
                        .thenComparing(Comparator.comparing(Student::getName))
                       .thenComparing(Comparator.naturalOrder()));   // 기본 정렬
        //System.out.println(resArr2);
        for(Student s : resArr2){
            System.out.println(s);
        }
 
        System.out.println("*****************");
        Stream<Student> stream = arr.stream().
                sorted(Comparator.comparing(Student::getBan).
                thenComparing(Comparator.comparing(Student::getName)).
                thenComparing(Comparator.naturalOrder()));
        stream.forEach(System.out::println);
 
        System.out.println("*****************");
        Stream<Student> studentStream = Stream.of(
                new Student("이순신"3300),
                new Student("김재동"1200),
                new Student("강감찬"2170),
                new Student("소지섭"2190),
                new Student("소유진"1200),
                new Student("남궁성"3270),
                new Student("신용권"3280)
        );
 
        studentStream.sorted(Comparator.comparing(Student::getBan) // 반별 정렬
                       .thenComparing(Comparator.naturalOrder()))  // 기본 정렬
                .forEach(System.out::println);
    }
}
 
class Student implements Comparable<Student> {
    String name;
    int ban;
    int totalScore;
 
    Student(String name, int ban, int totalScore) {
        this.name =name;
        this.ban =ban;
        this.totalScore =totalScore;
    }
 
    public String toString() {
        return String.format("[%s, %d, %d]", name, ban, totalScore);
    }
 
    String getName()     { return name;}
    int getBan()         { return ban;}
    int getTotalScore()  { return totalScore;}
 
    // 총점 내림차순을 기본 정렬로 한다.
    public int compareTo(Student s) {
        return s.totalScore - this.totalScore;
    }
}

 

Stream map 예제

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class StreamEX4 {
    public static void main(String[] args) {
        File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"),
                new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
        };
 
        Stream<File> fileStream = Stream.of(fileArr);
 
        // map()으로 Stream<File>을 Stream<String>으로 변환
        Stream<String> filenameStream = fileStream.map(File::getName);
        filenameStream.forEach(System.out::println); // 모든 파일의 이름을 출력
 
        System.out.println("-----------------------");
        fileStream = Stream.of(fileArr);  // 스트림을 다시 생성
 
        // peek : 스트림의 요소를 소비하지 않고 엿보기
        // map : 기존의 Stream 요소들을 변환하여 새로운 Stream을 형성하는 연산이다.
        // distinct는 중복된 데이터를 검사하기 위해 Object의 equals() 메소드 사용한다
        fileStream.map(File::getName)     // Stream<File> → Stream<String>
                .filter(s -> s.indexOf('.')!=-1)   // 확장자가 없는 것은 제외
                //.peek(s->System.out.printf("filename=%s%n",s))
                .map(s -> s.substring(s.indexOf('.')+1)) // 확장자만 추출
                //.peek(s->System.out.printf("extension=%s%n",s))
                .map(String::toUpperCase)     // 모두 대문자로 변환
                .distinct()   //  중복 제거
                .forEach(System.out::println); // 메소드참조(MethodReferences)
 
        System.out.println();
 
        // filter : 특정조건으로 스트림의 컨텐츠를 필터링하는 것
        // c로 시작하는 문자열만 필터링하도록 조건을 설정
        List<String> list = Arrays.asList("a1","a2","b1","b2","c2","c1","c3");
        Stream<String> stream1 = list.stream();
        final String filtered = stream1.filter(s -> s.startsWith("c")).
                collect(Collectors.joining(", "));
        System.out.println(filtered);
    }
}
 

 

 

'안드로이드 > Java 문법' 카테고리의 다른 글

[java] Wrapper Class  (0) 2021.11.18
[Java] Stream 예제 2  (0) 2021.11.16
[Java] Generics 예제1  (2) 2021.11.10
HashMap 예제  (0) 2021.10.30
[Java] HashSet 를 이용한 ArrayList 중복제거 및 정렬  (0) 2020.12.12
블로그 이미지

Link2Me

,
728x90

Generics(지네릭)은 컴파일 시 타입을 체크해주는 기능으로 JDK 1.5부터 적용되었다.

실행시 발생하는 에러를 컴파일 시 체크하여 에러 발생을 사전에 방지할 수 있다.

 

장점

1. 타입 안정성을 제공한다.

2. 타입체크와 형변환을 생략할 수 있어 코드가 간결해진다.

 

Generics 개념을 이해하는 예제이다.

import java.util.ArrayList;
import java.util.List;
 
class Product {}
class Tv extends Product {
    // Product Class는 Tv Class의 부모 클래스
}
class Audio extends Product {
    // Product Class는 Audio Class의 부모 클래스
}
 
public class GenericsEx1 {
    public static void main(String[] args) {
        // 일반 클래스 (JDK 1.5 이전 방식)
        ArrayList arrayList = new ArrayList();
        arrayList.add(new Tv());
        arrayList.add(new Audio());
 
        Tv t = (Tv) arrayList.get(0); // arrayList 의 첫번째 요소를 꺼낸다.
        // arrayList.get(0) 는 object 타입이므로 Tv 타입으로 형변환을 해줘야 한다.
        Audio audio = (Audio) arrayList.get(1); // arrayList 의 두번째 요소를 꺼낸다.
        // arrayList.get(1) 는 object 타입이므로 Audio 타입으로 형변환을 해줘야 한다.
 
        // 지네릭 클래스 : JDK 1.5 부터 적용
        ArrayList<Product> productList = new ArrayList<Product>();
        // 타입변수 : 참조변수의 타입 매개변수와 생성자의 타입 매개변수는 일치해야 한다.
        // 즉, 참조변수와 생성자의 대입된 타입은 일치해야 한다.
        // ArrayList : 원시타입, Product : 타입 변수 또는 타입 매개변수
        // ArrayList<Product> : 지네릭 클래스
        productList.add(new Tv());
        productList.add(new Audio());
 
        Tv t2 = (Tv) productList.get(0);
        // productList.get(0) 의 타입이 Product 이므로 불일치하여 형변환 해줘야 한다.
        Audio audio2 = (Audio) productList.get(1);
        // productList.get(1) 의 타입이 Product 이므로 불일치하여 형변환 해줘야 한다.
 
        printAll(productList);
 
        // 지네릭 클래스
        // JDK 1.7 부터 생성자에 타입변수 생략 가능
        ArrayList<Tv> tvList = new ArrayList<>();
        tvList.add(new Tv()); // Tv 타입의 객체만 저장 가능
        tvList.add(new Tv());
        //tvList.add(new Audio()); // Audio 는 TV의 자손이 아니므로 에러 발생
 
        //printAll(tvList); // 컴파일 에러가 발생한다.
        // new ArrayList<Tv>() 객체 타입 Tv과 printAll 메소드의 매개변수 타입 Product 불일치로 에러 발생
 
        // List 와 ArrayList 의 관계는 조상과 자손의 관계이며,
        // 조상의 참조변수로 자손의 객체 생성하는 것은 가능하다.(다형성)
        List<Tv> tvList1 = new ArrayList<Tv>(); // OK. 다형성
        tvList1.add(new Tv());
        tvList1.add(new Tv());
    }
 
    private static void printAll(ArrayList<Product> list) {
        for (Product p : list)
            System.out.println(p);
    }
}
 

 

 

 

블로그 이미지

Link2Me

,
728x90

HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션이다.

key와 value의 쌍으로 이루어진 데이터를 저장하는 구조로 이루어져 있다.

값은 중복 저장될 수 있지만 키는 중복 저장될 수 없다. 기존에 저장된 키와 동일한 키로 값을 저장하면 새로운 값으로 대치된다.

 

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
 
public class HashMapEx {
    public static void main(String[] args) {
        HashMap<Integer,String> map = new HashMap<>();
        map.put(1,"강아지"); //값 추가
        map.put(2,"고양이");
        map.put(3,"오소리");
        map.put(3,"호랑이"); // 값만 업데이트 된다. (키는 중복 없음)
 
        System.out.println("All key-value pairs");
        for(Integer key : map.keySet()) {
            System.out.println("{"+key+","+map.get(key)+"}");
        }
 
        // Foreach() 메소드로 순환하기
        map.forEach((key,value)-> {
            System.out.println("["+key+","+value+"]");
        });
 
        Iterator<Map.Entry<Integer, String>> entries = map.entrySet().iterator();
        while(entries.hasNext()){
            Map.Entry<Integer, String> entry = entries.next();
            System.out.println("[Key]:" + entry.getKey() + " [Value]:" +  entry.getValue());
        }
 
        Iterator<Integer> keys = map.keySet().iterator();
        // 특정 key값의 value를 가져오고싶다면 get(key)를 사용
        while(keys.hasNext()){
            int key = keys.next();
            System.out.println("[Key]:" + key + " [Value]:" +  map.get(key));
        }
 
        if(map.containsKey(2)){
            System.out.println" Selected Value :" +  map.get(2));
        }
    }
}
 

 

 

블로그 이미지

Link2Me

,
728x90

Person 클래스를 ArrayList 로 입력하고, HashSet 을 이용하여 데이터 중복을 제거하고 정렬하는 예제이다.

import java.util.*;
 
public class ArrayList_Person_DupRemove {
    public static void main(String[] args) {
        ArrayList<Person> arr = new ArrayList<>();
        arr.add(new Person("홍길동",25));
        arr.add(new Person("이순신",35));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("김수희",68));
        arr.add(new Person("홍길동",25));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("남진",75));
        arr.add(new Person("나훈아",72));
        arr.add(new Person("홍길동",29));
 
        // HashSet(Set인터페이스를 구현한 대표적인 컬렉션 클래스)을 사용하여 중복 제거하기
        Set<Person> arr2 = new HashSet<>(arr);
        ArrayList<Person> resArr2 = new ArrayList<>(arr2);
 
        System.out.println("중복값 제거후 결과값(순서없음) : " + resArr2.toString());
 
        // 추가한 순서를 유지하며 중복 제거하려면 LinkedHashSet클래스를 사용
        Set<Person> arr3 = new LinkedHashSet<>(arr);
        ArrayList<Person> resArr3 = new ArrayList<>(arr3);
 
        System.out.println("중복값 제거후 결과값(저장순서) : " + resArr3.toString());
 
        // ArrayList 정렬 시 Collections.sort() 메소드를 사용한다.
        Collections.sort(resArr2);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr2.toString());
 
        Collections.sort(resArr3);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr3.toString());
 
        // 역순 정렬
        Collections.sort(resArr2,new Descending());
        System.out.println("중복값 제거후 결과값(역순정렬) : " + resArr2.toString());
 
        Collections.sort(resArr3,new Descending());
        System.out.println("중복값 제거후 결과값(역순정렬) : " + resArr3.toString());
    }
}
 
class Descending implements Comparator {
    // 정렬 : 두 대상 비교하여 자리바꿈 반복
    @Override
    public int compare(Object o1, Object o2) {
        if(o1 instanceof Comparable && o2 instanceof Comparable) {
            Comparable c1 = (Comparable)o1;
            Comparable c2 = (Comparable)o2;
            return c1.compareTo(c2)* -1// -1을 곱해서 기본 정렬방식을 역으로 변경한다.
        }
        return -1;
    }
}
 
// equals()와 hashCode()를 오버라이딩해야 HashSet이 바르게 동작한다.
class Person implements Comparable<Person> {
    // 객체 정렬에 필요한 메소드(정렬기준 제공)를 정의한 인터페이스
    // Comparable : 기본 정렬기준을 구현하는데 사용
    // Comparator : 기준 정렬기준 외에 다른 기준으로 정렬하고자 할 때 사용
    String name;
    int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String toString() {
        return name + ":" + age;
    }
 
    @Override
    public int hashCode() {
        // int hash(Object... values); // 가변인자
        return Objects.hash(name,age);
        // 동일 객체에 대해 hashCode()를 여러 번 호출해도 동일한 값을 반환해야 한다.
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof Person)) return false;
 
        Person p = (Person) obj;
        // 나 자신(this)의 이름과 나이를 p와 비교
        return this.name.equals(p.name) && this.age == p.age;
        // equals()로 비교해서 true를 얻은 두 객체의 hashCode()값은 일치해야 한다.
    }
 
    // Comparable 인터페이스를 상속받아 compareTo() 함수를 오버라이딩
    @Override
    public int compareTo(Person p) {
        // 주어진 객체(p)를 자신(this)과 비교
        return this.name.compareTo(p.name);
    }
 
}
 
 

 

실행결과

 

ArrayList 로 입력한 데이터를 중복제거하고, 정렬하는 간단한 예제이다.

출처: https://link2me.tistory.com/1912 [소소한 일상 및 업무TIP 다루기]

나이순 정렬 기능 추가

import java.util.*;
 
public class ArrayList_Person_DupRemove {
    public static void main(String[] args) {
        ArrayList<Person> arr = new ArrayList<>();
        arr.add(new Person("홍길동",25));
        arr.add(new Person("이순신",35));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("김수희",68));
        arr.add(new Person("홍길동",25));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("남진",75));
        arr.add(new Person("나훈아",72));
        arr.add(new Person("홍길동",29));
 
        // HashSet(Set인터페이스를 구현한 대표적인 컬렉션 클래스)을 사용하여 중복 제거하기
        Set<Person> arr2 = new HashSet<>(arr);
        ArrayList<Person> resArr2 = new ArrayList<>(arr2);
 
        System.out.println("중복값 제거후 결과값(순서없음) : " + resArr2.toString());
 
        // 추가한 순서를 유지하며 중복 제거하려면 LinkedHashSet클래스를 사용
        Set<Person> arr3 = new LinkedHashSet<>(arr);
        ArrayList<Person> resArr3 = new ArrayList<>(arr3);
 
        System.out.println("중복값 제거후 결과값(저장순서) : " + resArr3.toString());
 
        // ArrayList 정렬 시 Collections.sort() 메소드를 사용한다.
        Collections.sort(resArr2);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr2.toString());
 
        Collections.sort(resArr3);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr3.toString());
 
        // 역순 정렬
        Collections.sort(resArr2,new Descending());
        System.out.println("중복값 제거후 결과값(역순정렬) : " + resArr2.toString());
 
        Collections.sort(resArr3,new Descending());
        System.out.println("중복값 제거후 결과값(역순정렬) : " + resArr3.toString());
 
        // 나이순 정렬
        Collections.sort(resArr2,(a, b) -> Person.AgeCompareTo(a,b));
        System.out.println("중복값 제거후 결과값(나이순서) : " + resArr2.toString());
 
        Collections.sort(resArr2,(a, b) -> Person.DescAgeCompareTo(a,b));
        System.out.println("중복값 제거후 결과값(나이역순) : " + resArr2.toString());
    }
}
 
class Descending implements Comparator {
    // 정렬 : 두 대상 비교하여 자리바꿈 반복
    @Override
    public int compare(Object o1, Object o2) {
        if(o1 instanceof Comparable && o2 instanceof Comparable) {
            Comparable c1 = (Comparable)o1;
            Comparable c2 = (Comparable)o2;
            return c1.compareTo(c2)* -1// -1을 곱해서 기본 정렬방식을 역으로 변경한다.
        }
        return -1;
    }
}
 
// equals()와 hashCode()를 오버라이딩해야 HashSet이 바르게 동작한다.
class Person implements Comparable<Person> {
    // 객체 정렬에 필요한 메소드(정렬기준 제공)를 정의한 인터페이스
    // Comparable : 기본 정렬기준을 구현하는데 사용
    // Comparator : 기준 정렬기준 외에 다른 기준으로 정렬하고자 할 때 사용
    String name;
    int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String toString() {
        return name + ":" + age;
    }
 
    @Override
    public int hashCode() {
        // int hash(Object... values); // 가변인자
        return Objects.hash(name,age);
        // 동일 객체에 대해 hashCode()를 여러 번 호출해도 동일한 값을 반환해야 한다.
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof Person)) return false;
 
        Person p = (Person) obj;
        // 나 자신(this)의 이름과 나이를 p와 비교
        return this.name.equals(p.name) && this.age == p.age;
        // equals()로 비교해서 true를 얻은 두 객체의 hashCode()값은 일치해야 한다.
    }
 
    // Comparable 인터페이스를 상속받아 compareTo() 함수를 오버라이딩
    @Override
    public int compareTo(Person p) {
        // 주어진 객체(p)를 자신(this)과 비교
        return this.name.compareTo(p.name);
    }
 
    // 나이순 정렬
    public static int AgeCompareTo(Person p1, Person p2){
        if(p1.age > p2.age) return 1;
        else if(p1.age < p2.age) return -1;
        else return 0;
    }
 
    // 나이 역순 정렬
    public static int DescAgeCompareTo(Person p1, Person p2){
        if(p1.age < p2.age) return 1;
        else if(p1.age > p2.age) return -1;
        else return 0;
    }
 
}
 

 

불필요한 코드를 정리한 예제

import java.util.*;
 
public class ArrayList_Person_DupRemove {
    public static void main(String[] args) {
        ArrayList<Person> arr = new ArrayList<>();
        arr.add(new Person("홍길동",25));
        arr.add(new Person("이순신",35));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("김수희",68));
        arr.add(new Person("홍길동",25));
        arr.add(new Person("강감찬",42));
        arr.add(new Person("남진",75));
        arr.add(new Person("나훈아",72));
        arr.add(new Person("홍길동",29));
        arr.add(new Person("유재석",25));
 
        // HashSet(Set인터페이스를 구현한 대표적인 컬렉션 클래스)을 사용하여 중복 제거하기
        Set<Person> arr2 = new HashSet<>(arr);
        ArrayList<Person> resArr2 = new ArrayList<>(arr2);
 
        System.out.println("중복값 제거후 결과값(순서없음) : " + resArr2.toString());
 
        // 추가한 순서를 유지하며 중복 제거하려면 LinkedHashSet클래스를 사용
        Set<Person> arr3 = new LinkedHashSet<>(arr);
        ArrayList<Person> resArr3 = new ArrayList<>(arr3);
 
        System.out.println("중복값 제거후 결과값(저장순서) : " + resArr3.toString());
 
        // ArrayList 정렬 시 Collections.sort() 메소드를 사용한다.
        Collections.sort(resArr2);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr2.toString());
 
        Collections.sort(resArr3);
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr3.toString());
 
 
        // 나이순 정렬
        Collections.sort(resArr2,Comparator.comparing(Person::getAge) // 나이순정렬
                .thenComparing(Comparator.naturalOrder())); // 기본정렬
        System.out.println("중복값 제거후 결과값(나이정렬) : " + resArr2.toString());
 
        // 역순 정렬
        Collections.sort(resArr2,Comparator.comparing(Person::getAge).reversed());
        System.out.println("중복값 제거후 결과값(나이역순) : " + resArr2.toString());
 
    }
}
 
// equals()와 hashCode()를 오버라이딩해야 HashSet이 바르게 동작한다.
class Person implements Comparable<Person> {
    // 객체 정렬에 필요한 메소드(정렬기준 제공)를 정의한 인터페이스
    // Comparable : 기본 정렬기준을 구현하는데 사용
    // Comparator : 기준 정렬기준 외에 다른 기준으로 정렬하고자 할 때 사용
    String name;
    int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public int getAge() {
        return age;
    }
 
    public String toString() {
        return name + ":" + age;
    }
 
    @Override
    public int hashCode() {
        // int hash(Object... values); // 가변인자
        return Objects.hash(name,age);
        // 동일 객체에 대해 hashCode()를 여러 번 호출해도 동일한 값을 반환해야 한다.
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof Person)) return false;
 
        Person p = (Person) obj;
        // 나 자신(this)의 이름과 나이를 p와 비교
        return this.name.equals(p.name) && this.age == p.age;
        // equals()로 비교해서 true를 얻은 두 객체의 hashCode()값은 일치해야 한다.
    }
 
    // Comparable 인터페이스를 상속받아 compareTo() 함수를 오버라이딩
    @Override
    public int compareTo(Person p) {
        // 주어진 객체(p)를 자신(this)과 비교
        return this.name.compareTo(p.name);
    }
 
}
 
 

 

'안드로이드 > Java 문법' 카테고리의 다른 글

[Java] Generics 예제1  (2) 2021.11.10
HashMap 예제  (0) 2021.10.30
[Java] HashSet 를 이용한 ArrayList 중복제거 및 단순 정렬  (0) 2020.12.12
[Java] HashSet 예제  (0) 2020.12.12
Java 와 C/C++ 비교  (0) 2020.11.08
블로그 이미지

Link2Me

,
728x90

ArrayList 로 입력한 데이터를 중복제거하고, 정렬하는 간단한 예제이다.

 

import java.util.*;
 
public class ArrayList_Remove_Dup {
    public static void main(String[] args) {
        ArrayList<String> arr = new ArrayList<String>();
        arr.add("홍길동");
        arr.add("강감찬");
        arr.add("이순신");
        arr.add("김두환");
        arr.add("홍길동");
        arr.add("이순신");
        arr.add("이순신");
        arr.add("권율");
 
        // HashSet(Set인터페이스를 구현한 대표적인 컬렉션 클래스)을 사용하여 중복 제거하기
        HashSet<String> arr2 = new HashSet<String>(arr);
        ArrayList<String> resArr2 = new ArrayList<String>(arr2);
 
        System.out.println("중복값 제거후 결과값(순서없음) : " + resArr2.toString());
 
        // 추가한 순서를 유지하며 중복 제거하려면 LinkedHashSet클래스를 사용
        Set<String> arr3 = new LinkedHashSet<>(arr);
        ArrayList<String> resArr3 = new ArrayList<String>(arr3);
 
        System.out.println("중복값 제거후 결과값(저장순서) : " + resArr3.toString());
 
        // ArrayList 정렬 시 Collections.sort() 메소드를 사용한다.
        Collections.sort(resArr2);
 
        System.out.println("중복값 제거후 결과값(정렬순서) : " + resArr2.toString());
    }
}
 

 

실행결과

 

'안드로이드 > Java 문법' 카테고리의 다른 글

HashMap 예제  (0) 2021.10.30
[Java] HashSet 를 이용한 ArrayList 중복제거 및 정렬  (0) 2020.12.12
[Java] HashSet 예제  (0) 2020.12.12
Java 와 C/C++ 비교  (0) 2020.11.08
[Java] 람다식(Lambda Expression)  (0) 2020.07.26
블로그 이미지

Link2Me

,
728x90

HashSet : 중복해서 저장하지 않는 집합(Set)으로 사용할 수 있는 클래스

HashSet은 객체를 저장하기 전에 기존에 같은 객체가 있는지 확인한다.

같은 객체가 없으면 저장하고, 있으면 저장하지 않는다.

import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
 
public class HashSetEX {
    public static void main(String[] args) {
        HashSet set = new HashSet();
 
        // HashSet은 객체를 저장하기전에 기존에 같은 객체가 있는지 확인한다.
        // 같은 객체가 없으면 저장하고, 있으면 저장하지 않는다.
        set.add("abc");
        set.add("abc");
 
        // boolean add(Object o)는 저장할 객체의 equals()와 hashCode()를 호출
        // equals()와 hashCode()가 오버라이딩 되어 있어야 한다.
        set.add(new PerSon("홍길동",33));
        set.add(new PerSon("이순신",35));
        set.add(new PerSon("홍길동",33));
 
        System.out.println(set);
 
        // Iterator : 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화한 것
        Iterator iterator = set.iterator(); // 값을 꺼내는 메소드를 이용한다.
        while (iterator.hasNext()) { // 읽어올 요소가 남아 있는가?
            System.out.println(iterator.next()); // 다음 요소를 읽어온다.
        }
        // HashSet는 데이터에 순서가 없기 때문에 데이터를 순서대로 읽어오거나,
        // 특정 위치의 데이터를 읽어올 수 있는 방법이 없기 때문에
        // "Iterator" 메소드를 활용해서 집합에 있는 "전체" 데이터를 불러올 수 있다.
    }
 
}
 
// equals()와 hashCode()를 오버라이딩해야 HashSet이 바르게 동작한다.
class PerSon {
    String name;
    int age;
 
    public PerSon(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String toString() {
        return name + ":" + age;
    }
 
    @Override
    public int hashCode() {
        // int hash(Object... values); // 가변인자
        return Objects.hash(name,age);
        // 동일 객체에 대해 hashCode()를 여러 번 호출해도 동일한 값을 반환해야 한다.
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof PerSon)) return false;
 
        PerSon p = (PerSon) obj;
        // 나 자신(this)의 이름과 나이를 p와 비교
        return this.name.equals(p.name) && this.age == p.age;
        // equals()로 비교해서 true를 얻은 두 객체의 hashCode()값은 일치해야 한다.
    }
 
}
 

 

 

실행결과

 

블로그 이미지

Link2Me

,
728x90

Java 언어와 C/C++ 비교를 표로 정리했다.

언어

차이점 설명

C언어

절차지향 언어

Java 보다 처리 속도가 빠르다

소스를 컴파일하고 나면 플랫폼상에서 바로 실행될 수 있는 실행파일이 생성된다.

컴파일러가 중간 단계인 오브젝트 코드를 생성한 후, 링커가 필요한 라이브러리를 링크하여 최종 실행 가능한 실행파일을 생성한다.

오브젝트 코드 및 실행파일은 플랫폼에 따라 다르므로 플랫폼이 달라지면 컴파일 및 링크를 새로 해야한다. 

절차지향은 대부분 코드가 main메소드에 한방에 정의 됨

절차지향 언어는 클래스 구분없이 한줄 한줄 절차적으로 컴파일

하드웨어를 직접 제어할 수 있는 것이 C언어

포인터를 이용하여 메모리에 접근한다.

관련된 데이터를 하나로 모으기 위해 구조체(structure)나 공용체(union)을 사용한다.

C++

객체지향언어. 동적 메모리 직접 관리. C++의 메모리는 일반적으로 스마트 포인터가 관리한다.

소스를 컴파일하고 나면 플랫폼상에서 바로 실행될 수 있는 실행파일이 생성된다.

컴파일러가 중간 단계인 오브젝트 코드를 생성한 후, 링커가 필요한 라이브러리를 링크하여 최종 실행 가능한 실행파일을 생성한다.

오브젝트 코드 및 실행파일은 플랫폼에 따라 다르므로 플랫폼이 달라지면 컴파일 및 링크를 새로 해야한다. 

C++에서 new 키워드를 통해 객체를 생성하면 Heap영역에 객체가 할당되고, 일반적인 변수 선언 방식을 택하면 Stack영역에 할당이 된다.

Java 보다 처리 속도가 빠르다

객체지향은 여러 Class로 나누어 개발이 가능하다.

C++ 은 다중 상속이 가능하다. C++ 은 Interface는 지원하지 않는다.

값에 의한 매개변수 전달 또는 참조에 의한 매개변수 전달 

C++는 기본적으로 char 형이 1byte이며 부호 표시 변수 이기때문에 -129~127 까지 표현이 가능하고 ASCII 문자형을 표현한다.

C++은 이름 공간 레벨에 상수, 변수, 함수가 있을 수 있다.

C++에 있는 const는 개념적으로 '읽기 전용' 데이터임을 명시하며 자료형에 적용된다.

C++은 임의의 블록 크기로 메모리를 할당할 수 있다. 

C++에서는 초기화하지 않고 객체를 생성할 수 있다(쓰레기 값을 가지고 있다).

C++에는 메모리에 할당하였지만 접근 가능한 참조가 없는 객체가 있을 수 있다.

이런 접근 불가능한 객체는 소멸될 수도 없으므로 메모리 누수를 일으킨다.

C++은 C와 가장 하위 호환성이 좋은 언어이다.

C 라이브러리에 있는 운영 체제의 API 같은 것을 C++에서는 직접 사용할 수 있다.

C++은 보통 기계어로 직접 컴파일되고, 이를 운영 체제가 실행한다.

C++에서는 연산자오버로딩을 지원하지만, JAVA는 그렇지 않다.

Java

객체지향언어(Object Oriented Programming). 동적 메모리 가비지 컬렉션이 관리한다.

컴파일하면 JVM에서 실행가능한 바이트코드 형태의 클래스 파일이 생성되고, JVM을 통해 실행할 수 있다.

자바는 링크 과정이 없이 컴파일러가 바로 바이트 코드를 생성한다.

런타임에 필요한 클래스들이 JVM에 링크되며 클래스 로더가 동적으로 필요한 클래스를 로딩한다.

플랫폼 독립적이므로, 자바의 클래스 파일이 JVM에 실행되기 때문에 플랫폼이 달라져도 다시 컴파일하지 않아도 된다.

JAVA는 객체를 메모리의 Heap영역에만 할당할 수 있으나, C++의 경우 Heap과 Stack영역 모두에 할당이 가능하다.

자바는 c++ 와 달리 포인터를 지원하지 않고, 레퍼런스 만을 지원한다.

자바의 자료형은 크게 기본형과 레퍼런스 타입으로 분류할 수 있다.

레퍼런스 타입은 new 연산자를 통해서 heap 영역에 생성되는 자료형들을 의미한다. 레퍼런스 타입으로는 클래스, 배열, 인터페이스 가 있다. 

Java는 char형이 2byte 이고 unsigned 부호표시 불가이기 때문에 0~65535까지 표현이 가능하고 유니코드를사용한다.

C++ 는 float, double, long double을 지원하는데 Java 는 float, double을 지원한다. 

C++ 는 문자열을 위한 기본 자료형을 지원하지않는다. 대신 배열을 사용한다. 

Java는 문자열을 위한 기본형을 없지만 string이라는 클래스를 제공한다. 

JAVA는 익명클래스(Anonymous Class)를 지원하지만, C++의 경우 그렇지 않다.

JAVA는 동적바인딩(런타임 중 실행코드가 결정한다), C++은 정적바인딩(virtual 키워드로 동적바인딩 가능)

Java코드는 실행하면서 JVM인터프리터에의해 바이트코드를 기계코드로 변환한다.

객체지향은 여러 Class로 나누어 개발이 가능하다.

단일 상속을 지원하고, 하나의 Class가 여러개의 Interface를 Implement할 수 있다.

클래스와 메인을 왔다갔다하면서 읽어야 하기 때문에 해석하기가 악갼 복잡하다.

항상 값에 의한 매개변수 전달(call by value).  매개변수로 객체에 대한 참조값을 사용할 수는 있다.

자바에서는 모든 상수, 변수, 함수는 반드시 클래스나 인터페이스에 속해 있어야 한다.

자바에 있는 final은 변수가 다시 할당될 수 없음을 나타낸다. 

자바는 객체를 생성하는 방식으로만 메모리를 할당할 수 있다. 

자바는 기본 초기화가 강제로 수행된다(0 등으로 초기화 된다).

자바에서는 접근 불가능한 객체가 되기 전까지 그 객체는 가비지 콜렉터가 소멸시키지 않는다.

자바는 메모리가 아닌 다른 자원의 누수에 대해서는 C++에 비해 상대적으로 취약하다.

자바에서 운영 체제나 하드웨어 기능에 직접 접근하려면 자바 네이티브 인터페이스를 이용하여야만 한다.

자바는 보통 바이트코드로 컴파일되고, 자바 가상 머신이 인터프리터 방식으로 실행하거나

 JIT 컴파일러 방식으로 기계어로 컴파일한 다음 실행한다.


블로그 이미지

Link2Me

,
728x90

y = f(x) 형태의 함수로 구성된 프로그래밍 기법

Java 8에서 함수형 프로그래밍 방식인 람다식을 지원한다.

객체지향 프로그래밍보다는 함수형 프로그래밍에 가깝다고 할 수 있다.

 

람다식이란? 함수(메서드)를 간단한 ‘식(Expression)’으로 표현하는 방법

 

- 함수는 클래스에 독립적, 메소드는 클래스에 종속적
- 메서드의 이름과 반환타입을 제거하고 ‘->’를 블록{} 앞에 추가한다.
- 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에‘;’안 붙임)
- 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)
- 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
- 블록 안의 문장이 하나뿐 일 때, 괄호{}생략가능(끝에 ‘;’ 안 붙임)
- 단, 하나뿐인 문장이 return문이면 괄호{} 생략불가

 

 

람다식 장점
1. 코드를 간결하게 만들 수 있다.
2. 코드가 간결하고 식에 개발자의 의도가 명확히 드러나므로 가독성이 향상된다.
3. 함수를 만드는 과정없이 한번에 처리할 수 있기에 코딩하는 시간이 줄어든다.
4. 병렬프로그래밍이 용이하다.

람다식 단점
1. 람다를 사용하면서 만드는 익명함수는 재사용이 불가능하다.
2. 디버깅이 다소 까다롭다.
3. 람다를 남발하면 코드가 지저분해질 수 있다.
4. 재귀로 만들경우에는 다소 부적합한면이 있다.

 

 
@FunctionalInterface
interface MyFunction {
    // 함수형 인터페이스 : 단 하나의 추상 메소드만 선언된 인터페이스
    int max(int a, int b);  // public abstract 생략
}
 
public class LambdaEX1 {
    public static void main(String[] args) {
        // 람다식(익명 객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 한다.
        MyFunction f = (a, b) -> a > b ? a : b;
        int value = f.max(3,5);
        System.out.println(value);
    }
}
 

 

@FunctionalInterface
interface MyFunctionalInterface{
    // 함수형 인터페이스 : 단 하나의 추상 메소드만 선언된 인터페이스
    void method();
}
 
public class LambdaEX2 {
    public static void main(String[] args) {
        MyFunctionalInterface fi1 = new MyFunctionalInterface() {
            @Override
            public void method() {
                String str = "직접 익명 객체 구현함";
                System.out.println(str);
            }
        };
        fi1.method();
 
        MyFunctionalInterface fi2 = () -> {
            String str = "람다식 익명 객체 구현함";
            System.out.println(str);
        };
        fi2.method();
    }
}
 

 

 

 

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
@FunctionalInterface
interface MyFunctionIF {
    // 함수형 인터페이스 : 단 하나의 추상 메소드만 선언된 인터페이스
    int max(int a, int b);  // public abstract 생략
}
 
public class LambdaEX1 {
    public static void main(String[] args) {
        // 람다식(익명 객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 한다.
        MyFunctionIF f = (a, b) -> a > b ? a : b;
        int value = f.max(3,5);
        System.out.println(value);
 
        int result = Integer.parseInt("123");
        System.out.println(result);
 
        System.out.println("===================================");
        ArrayList<Integer> list = new ArrayList<>();
        for(int i=0;i < 20;i++)
            list.add(i);
 
        System.out.println(list);
 
        // list의 모든 요소를 출력
        list.forEach(i->System.out.print(i + ", "));
        System.out.println();
 
        // list에서 2 또는 3의 배수를 제거한다.
        list.removeIf(x -> x%2==0 || x%3==0);
        System.out.println(list);
 
        list.replaceAll(i->i*3); // list의 각 요소에 3을 곱한다.
        System.out.println(list);
 
        System.out.println("===================================");
        Map<StringString> map = new HashMap<>();
        map.put("1""1");
        map.put("2""2");
        map.put("3""3");
        map.put("4""4");
 
        // map의 모든 요소를 {k,v}의 형식으로 출력한다.
        map.forEach((k,v)-> System.out.print("{"+k+","+v+"},"));
        System.out.println();
    }
}
 

 

메소드 참조

메소드 레퍼런스(Method Reference)는 Lambda 표현식을 더 간단하게 표현하는 방법이다.

메소드 레퍼런스는 ClassName::MethodName 형식으로 입력한다. 
메소드를 호출하는 것이지만 괄호()는 써주지 않고 생략한다.

import java.util.Arrays;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.IntBinaryOperator;
 
class MathBox {
    // 클래스::메소드 --> 정적(static) 메소드 참조
    public static int staticPlus(int x, int y){
        return x+y;
    }
 
    // 참조변수::메소드 --> 인스턴스 메소드 참조
    public int sum(int x, int y){
        return x+y;
    }
 
    // 정수 빼기
    public int sub(int x, int y){
        return x-y;
    }
 
    // 정수 나누기
    public int division(int x, int y){
        return x / y;
    }
 
    // 실수 나누기
    public Float float_div(Float x, Float y){
        return x / y;
    }
 
}
 
public class Method_References {
    public static void main(String[] args) {
        List<String> strList = Arrays.asList("안녕""하세요");
        strList.stream().forEach(System.out::println);
 
        // static 메소드 참조
        IntBinaryOperator operator1;
        // 메소드 참조해서 매개변수 리턴타입 알아내 람다식에서 불필요한 매개변수 제거 목적
        operator1 = MathBox::staticPlus; // 메소드 참조
        System.out.println(operator1.applyAsInt(53));
 
        operator1 = (x, y) -> MathBox.staticPlus(x, y);
        System.out.println(operator1.applyAsInt(35));
 
        // instance 메소드 참조
        MathBox calculator = new MathBox();
        IntBinaryOperator operator2 = calculator::sum; // 메소드 참조
        System.out.println("더하기 : " + operator2.applyAsInt(1132));
 
        operator1 = (x, y) -> calculator.sum(x, y);
        System.out.println(operator1.applyAsInt(1132));
 
        operator2 = calculator::sub; // 메소드 참조
        System.out.println("빼기 : " + operator2.applyAsInt(1130));
 
        operator2 = calculator::division; // 메소드 참조
        System.out.println("나누기 : " + operator2.applyAsInt(154));
 
        BinaryOperator<Integer> operator3 = (n1, n2) -> n1 + n2; // 타입 추론
        Integer sum = operator3.apply(10100);
        System.out.println(sum);
 
        BinaryOperator<Float> operator4 = calculator::float_div;
        System.out.println("나누기 : " + operator4.apply(15.0f, 4.0f));
 
        BinaryOperator<Float> operator5 = (n1, n2) -> n1 / n2; // 타입 추론
        Float div = operator5.apply(15.0f, 4.0f);
        System.out.println(div);
    }
}
 

 

 

자바에서 제공하는 함수형 인터페이스를 이용하면 인터페이스를 만들지 않고 간단하게 코드 구현 가능하다.

import java.util.function.Consumer;
 
interface Printable {
    void doSomething(String text);
}
 
public class MethodReferencesEX2 {
    public static class Printer {
        static void printSomething(String text) {
            System.out.println(text);
        }
    }
 
    public static void main(String[] args) {
        Printable printable = text -> Printer.printSomething(text);
        printable.doSomething("do something");
 
        Printable printable2 = Printer::printSomething;
        printable2.doSomething("do something");
 
        // Consumer<클래스>는 자바에서 제공하는 함수형 인터페이스
        // String 1개를 인자로 받아 void를 리턴하는 메소드를 갖고 있는 인터페이스
        Consumer<String> consumer = Printer::printSomething;
        consumer.accept("do something"); // Printable 인터페이스를 만들 필요가 없다.
    }
}
 

 

블로그 이미지

Link2Me

,
728x90

다형성(Polymorphism)

- 같은 타입이지만 실행 결과가 다양한 객체 대입 가능한 성질

- 조상 타입의 참조변수로 자손 타입의 객체를 다룰 수 있는 것이 다형성

 

예제1)

public class Ex7_7 {
    public static void main(String[] args) {
        // 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것이 다형성
        // 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
        Car car = null; // 조상 타입의 참조변수
        FireEngine fe = new FireEngine(); // 실제 인스턴스가 무엇인지가 중요
       
        car = fe; // 자손 → 조상으로 형변환. 형변환 생략됨
        car.drive();
       
        FireEngine fe2 = null; // 자손 타입의 참조변수
        fe2 = (FireEngine) car; // 조상 → 자손으로 형변환. 형변환 생략불가.
        fe2.drive();
        fe2.water();
    }
}

class Car {
    String color;
    int door;
   
    void drive() {
        System.out.println("운전중!");
    }
   
    void stop() {
        System.out.println("정지.");
    }
}

class FireEngine extends Car {
    void water() {
        System.out.println("물 뿌리는 중!");
    }
}
 

 

예제1은 문제없이 잘 동작된다.

예제2는 자손 타입의 참조변수로 조상 타입의 인스턴스를 참조할 수 없다는 사항에 위배된다.

 

예제2)

public class Ex7_7 {
    public static void main(String[] args) {
        // 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것이 다형성
        // 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
        Car car = new Car(); // 조상 인스턴스
        FireEngine fe = (FireEngine) car; // Car cannot be cast to FireEngine
        fe.drive();
    }
}

class Car {
    String color;
    int door;
   
    void drive() {
        System.out.println("운전중!");
    }
   
    void stop() {
        System.out.println("정지.");
    }
}

class FireEngine extends Car {
    void water() {
        System.out.println("물 뿌리는 중!");
    }
}
 

 

예제3)

public class Polymorphism {
    public static void main(String[] args) {
        // 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것이 다형성
        // 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
        Car car = null// 조상 타입의 참조변수
        FireEngine fe = new FireEngine(); // 실제 인스턴스가 무엇인지가 중요
 
        car = fe; // 자손 → 조상으로 형변환. 형변환 생략됨
        car.drive(); // 부모 타입에 선언된 변수와 메소드만 사용 가능
 
        FireEngine fe2 = null// 자손 타입의 참조변수
        if(car instanceof FireEngine){ // 먼저 자식 타입인지 확인 후 강제 타입 실행.
            System.out.println("===== 객체 타입 확인 =====");
            fe2 = (FireEngine) car; // 조상 → 자손으로 형변환. 형변환 생략불가
            fe2.drive();
            fe2.water(); // 자식 타입에 선언된 변수와 메소드를 다시 사용해야 하는 경우
        }
 
        Car car1 = new FireEngine(); // 부모 클래스 변수 = 자식 클래스 타입;
        car1.drive(); // 변환 후에는 부모 클래스 멤버만 접근 가능
 
        System.out.println("===== 상속 처리 =====");
        FireEngine fe3 = new FireEngine();
        fe3.drive();
        fe3.water();
 
        System.out.println("===== 부모 타입 =====");
        Car car2 = new Car();
        // 자식타입의 참조변수로 부모타입의 인스턴스를 참조할 수는 없다.
        FireEngine fe4 = (FireEngine) car2;
        fe4.drive();
 
    }
}
 
class Car {
    String color;
    int door;
 
    void drive() {
        System.out.println("운전중!");
    }
 
    void stop() {
        System.out.println("정지.");
    }
}
 
class FireEngine extends Car {
    void water() {
        System.out.println("물 뿌리는 중!");
    }
}
 

 

 

예제4)

상위클래스 타입의 변수가 복수의 하위클래스 객체를 참조할 수 있도록 하는 것을 다형성이라 한다.

- Product 가 상위 클래스, Tv, Computer, Audio 는 하위 클래스

import java.util.ArrayList;
 
class Product {
    int price;            // 제품의 가격
    int bonusPoint;    // 제품구매 시 제공하는 보너스점수
 
    public Product(int price) {
        this.price = price;
        this.bonusPoint = (int)(price / 10.0);
    }
 
    public Product() {
    }
}
 
class Tv extends Product {
    public Tv() {
        // 조상 클래스의 생성자 Product(int price)를 호출.
        super(100);
    }
 
    @Override
    public String toString(){
        // Object 클래스의 toString()을 오버라이딩한다.
        return "Tv";
    }
}
 
class Computer extends Product {
    public Computer() {
        // 조상 클래스의 생성자 Product(int price)를 호출.
        super(200);
    }
 
    @Override
    public String toString() {
        // Object 클래스의 toString()을 오버라이딩한다.
        return "Computer";
    }
}
 
class Audio extends Product {
    public Audio() {
        // 조상 클래스의 생성자 Product(int price)를 호출.
        super(50);
    }
 
    @Override
    public String toString() {
        // Object 클래스의 toString()을 오버라이딩한다.
        return "Audio";
    }
}
 
class Buyer {
    int money = 1000;      // 소유금액
    int bonusPoint = 0// 보너스점수
    ArrayList<Product> cart = new ArrayList<>();// 구입한 제품을 저장하기 위한 배열
 
    void buy(Product p) { // 매개 변수가 Product 타입의 참조변수
        if(money < p.price) {
            System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
            return;
        }
 
        money -= p.price;             // 가진 돈에서 구입한 제품의 가격을 뺀다.
        bonusPoint += p.bonusPoint;   // 제품의 보너스 점수를 추가한다.
        cart.add(p);                // 제품을 ArrayList<Product> cart에 추가한다.
        System.out.print(p + "를 구입하셨습니다.");
        System.out.println("현재 잔액은 " + money + " 만원입니다.");
    }
 
    void summary() {                  // 구매한 물품에 대한 정보를 요약해서 보여 준다.
        int sum = 0;                 // 구입한 물품의 가격합계
        String itemList ="";         // 구입한 물품목록
 
        // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
        for(int i=0; i < cart.size();i++) {
            sum += cart.get(i).price;
            if(i == cart.size() -1){
                itemList += cart.get(i) + " ";
            } else {
                itemList += cart.get(i) + ", ";
            }
        }
        System.out.println("구입하신 물품의 총 금액은 " + sum + "만원입니다.");
        System.out.println("구입하신 제품은 " + itemList + "입니다.");
    }
}
 
public class Ploymorphism {
    public static void main(String[] args) {
        Buyer b = new Buyer();
 
        b.buy(new Tv());
        b.buy(new Computer());
        b.buy(new Audio());
        b.summary();
    }
}
 

 

 

블로그 이미지

Link2Me

,
728x90

패키지의 선언
-
패키지는 소스파일의 첫 번째 문장으로 단 한번 선언
-
같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다.
-
패키지 선언이 없으면, 이름없는 패키지에 속하게 된다.

- 패키지명은 선언시 반드시 소문자로 선언해야 한다.


import
-
클래스를 사용할 때 패키지 이름을 생략할 수 있다.
-
컴파일러에게 클래스가 속한 패키지를 알려준다.
-
java.lang 패키지의 클래스는 import 하지 않고도 사용할 수 있다.
- import
문은 패키지문과 클래스 선언의 사이에 선언한다.

접근 제어자

제어자

같은 클래스

같은 패키지

자손 클래스

전체

 public





 protected



 

 (default)

 

 

private

 

 

 


아래 코드는 Eclipse 툴이 아니라 Android Studio 에서 Android 어플 작성을 위한 코드 생성으로 테스트했다.

개념은 동일하므로 Eclipse, Android Studio 상관 없다.

package com.link2me.android.pkg1;

public class MyParent { // 접근 제어자가 public
    private int prv; // 같은 클래스
    int dft; // 같은 패키지
    protected int prt; // 같은/다른 패키지 + 자손
    public int pub; // 접근 제한 없음

    public void printMembers() {
        System.out.println(prv); // OK, 같은 클래스
        System.out.println(dft); // OK, 같은 클래스
        System.out.println(prt); // OK, 같은 클래스
        System.out.println(pub); // OK, 같은 클래스
    }
}

class MyParentTest { // 접근제어자가 default
    // 하나의 파일에 public class 이름이 2개 오면 에러가 발생한다.
    // 같은 패키지
    public void printMembers(){
        MyParent mp = new MyParent();
        //System.out.println(mp.prv); // 에러, private은 같은 class 내에서만 접근 가능
        System.out.println(mp.dft); // OK, default는 같은 패키지내에서 접근 가능
        System.out.println(mp.prt); // OK, protected는 같은/다른 패키지 + 자손
        System.out.println(mp.pub); // OK, public은 접근 제한이 전혀 없다
    }

package com.link2me.android.pkg2;

import com.link2me.android.pkg1.MyParent;

public class MyChild extends MyParent { // 상속 받음
    // 다른 패키지 + 자손
    public void printMembers(){
        //System.out.println(prv); // 에러, private은 같은 class 내에서만 접근 가능
        //System.out.println(dft); // 에러, default는 같은 패키지내에서만 접근 가능
        System.out.println(prt); // OK, protected는 같은/다른 패키지 + 자손
        System.out.println(pub); // OK, public은 접근 제한이 전혀 없다
    }
}

class MyParentTest { // 접근제어자가 default
    // 하나의 파일에 public class 이름이 2개 오면 에러가 발생한다.
    // 다른 패키지 + 다른 class
    public void printMembers(){
        MyParent mp = new MyParent();
        //System.out.println(mp.prv); // 에러 private은 같은 class 내에서만 접근 가능
        //System.out.println(mp.dft); // 에러, default는 같은 패키지내에서만 접근 가능
        //System.out.println(mp.prt); // 에러, protected는 같은/다른 패키지 + 자손
        System.out.println(mp.pub); // OK, public은 접근 제한이 전혀 없다
    }
}


블로그 이미지

Link2Me

,
728x90

Java Thread Synchronized(동기화)란 여러 개의 Thread가 한 개의 자원을 사용하고자 할 때,
해당 Thread만 제외하고 나머지는 접근을 못하도록 막는 것이다.


Multi-Thread로 인하여 동기화를 제어해야하는 경우가 생긴다.
synchronized 키워드를 사용하면, Multi-Thread 상태에서 동일한 자원을 동시에 접근하게 되었을 때 동시 접근을 막게 된다.
synchronized를 사용하는 방법은 아래와 같다.
1. 메서드에 synchronized 하기

    - synchronized 를 붙이면 메소드 전체가 임계 영역으로 설정된다.

    - 쓰레드는 synchronized 메소드가 호출된 시점부터 해당 메소드가 포함된 객체의 Lock을 얻어 작업을 수행하다가 메소드가 종료되면 Lock 을 반환한다.
2. 블록에 synchronized 하기

   - 메소드 내의 코드 일부를 블럭{} 으로 감싸고 블럭 앞에 synchronized를 붙이는 것이다.

   - 이때 참조변수는 Lock 을 걸고자 하는 객체를 참조하는 것이어야 한다.

   - 이 영역 안으로 들어가면서부터 쓰레드는 지정된 객체의 Lock 을 얻게 되고, 이 블럭을 벗어나면 Lock 을 반납한다.


synchronized : 단 하나의 쓰레드만 실행할 수 있는 메소드 또는 블록을 말한다.
- 다른 쓰레드는 메소드나 블록이 실행이 끝날 때까지 대기해야 한다.
- wait(), notify(), notifyAll() 은 동기화 메소드 또는 블록에서만 호출 가능한 Object의 메소드
  두개의 쓰레드가 교대로 번갈아 가며 실행해야 할 경우에 주로 사용한다.
- wait() 호출한 쓰레드는 일시 정지가 된다.
- notify() or notifyAll()을 호출하면
다른 쓰레드가 실행 대기 상태가 된다.

- synchronized를 대충 사용하면 퍼포먼스 저하, 예상치 못한 동작이 발생할 수 있다.

- 임계 영역은 멀티쓰레드 프로그램의 성능을 좌우하기 때문에 가능하면 메소드 전체에 Lock을 거는 것보다 synchronized 블럭으로 임계 영역을 최소화해서 보다 효율적인 프로그램이 되도록 노력해야 한다.

- 쓰레드는 자신의 run() 메소드가 모두 실행되면 자동적으로 종료된다.




아래 예제를 실행해서 결과가 나오는 걸 이해해야 한다.

synchronized 를 붙인 경우와 붙이지 않은 경우에 결과를 비교해보면 명확하게 이해가 될 것이다.


예제1

public class SynchThread extends Thread {
    int total = 0;

    @Override
    public void run() {
        synchronized (this) { // 해당 객체(this)에 Lock 이 걸린다.
            for (int i = 0; i < 5; i++) {
                System.out.println(i + "를 더한다.");
                total += i;

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            notify(); // 위 작업이 모두 끝나면 notify()를 호출하여 다른 쓰레드를 실행 대기 상태로 만든다.
        }
    }
}

public class SyncMainThread {

    public static void main(String[] args) {
        SynchThread syncThread = new SynchThread();
        syncThread.start();
       
        synchronized (syncThread) {
            System.out.println("syncThread 가 완료될 때까지 기다린다.");
            try {
                syncThread.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Total Sum : " + syncThread.total);
        }
    }

}

실행결과

syncThread 가 완료될 때까지 기다린다.
0를 더한다.
1를 더한다.
2를 더한다.
3를 더한다.
4를 더한다.
Total Sum : 10



예제2 (동기화 개념을 명확히 이해하고자 한다면 아래 동영상 강좌를 꼭 들어라)

두개의 쓰레드가 교대로 번갈아 가며 실행해야 하는 경우에 주로 사용한다.

"이것이 자바다" 유투브 강좌 https://www.youtube.com/watch?v=hao05jNL2m8 35분부터 참조하면 설명이 잘 되어 있다. 아래 코드는 https://www.youtube.com/watch?v=7pNZr8cmjis 강좌에 나온 내용이다.


- notify() 는 현재 waiting 된 다른 쓰레드를 실행대기 상태로 만든다.

- wait() 를 호출해서 자기 자신은 일시 정지가 된다.


public class DataBox {
    private String data;

    public synchronized String getData() { // 소비자 쓰레드가 호출
        if (this.data == null) { // 데이터를 읽을 수 없으니까 wait 으로 만든다.
            try {
                wait(); // 생성자 쓰레드가 데이터를 넣어줄 때가지 일시 정지된다.

            } catch (InterruptedException e) {
            }
        }
        String returnValue = data;
        System.out.println("ConsummerThread가 읽은 데이터: " + returnValue);
        data = null; // 데이터를 읽었으니까 null 로 만든다.
        notify(); //
notify()를 호출해서 현재 일시정지 상태에 있는 생성자 쓰레드를 실행 대기 상태로 만든다.
        return returnValue;
    }

    public synchronized void setData(String data) { // 생성자 쓰레드가 호출
        if (this.data != null) { // 소비자 쓰레드가 아직 데이터를 읽지 않았다면
            try {
                wait(); //

            } catch (InterruptedException e) {
            }
        }
        this.data = data;
// 데이터를 공유 객체에 저장한다.

        System.out.println("ProducerThread가 생성한 데이터: " + data);
        notify(); // notify()를 호출해서 소비자 쓰레드를 실행 대기 상태로 만들어 data를 읽어갈 수 있게 한다.

    }
}

public class ProducerThread extends Thread {
    private DataBox dataBox;

    public ProducerThread(DataBox dataBox) {
        this.dataBox = dataBox;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            String data = "Data-" + i;
            dataBox.setData(data);
        }
    }

}

public class ConsumerThread extends Thread {
    private DataBox dataBox;

    public ConsumerThread(DataBox dataBox) {
        this.dataBox = dataBox;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            String data = dataBox.getData();
        }
    }

}

public class WaitNotifyExam {

    public static void main(String[] args) {
        DataBox dataBox = new DataBox();

        ProducerThread producerThread = new ProducerThread(dataBox);
        ConsumerThread consumerThread = new ConsumerThread(dataBox);

        producerThread.start();
        consumerThread.start();
    }
}

실행 결과

ProducerThread가 생성한 데이터: Data-1
ConsummerThread가 읽은 데이터: Data-1
ProducerThread가 생성한 데이터: Data-2
ConsummerThread가 읽은 데이터: Data-2
ProducerThread가 생성한 데이터: Data-3
ConsummerThread가 읽은 데이터: Data-3


자바의 정석 13장 예제

public class SyncEx {

    public static void main(String[] args) {
        Runnable runnable = new RunnableSync();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }

}

class Account {
    private int balance = 1000; // private으로 해야 동기화 의미가 있다.

    public  int getBalance() {
        return balance;
    }

    public synchronized void withdraw(int money){ // synchronized로 메서드를 동기화
        if(balance >= money) {
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {}
            balance -= money;
        }
    } // withdraw
}

class RunnableSync implements Runnable {
    Account acc = new Account();

    public void run() {
        while(acc.getBalance() > 0) {
            // 100, 200, 300중의 한 값을 임으로 선택해서 출금(withdraw)
            int money = (int)(Math.random() * 3 + 1) * 100;
            acc.withdraw(money);
            System.out.println("balance:"+acc.getBalance());
        }
    } // run()
}
 


블로그 이미지

Link2Me

,
728x90

쓰레드는 자신의 run() 메소드가 모두 실행되면 자동적으로 종료된다.

하지만, 경우에 따라서는 실행 중인 쓰레드를 즉시 종료할 필요가 있다.

public class StopThread extends Thread {
    public void run() {
        while(true) {
            System.out.println("실행 중");
        }
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
}

StopThread는 while(true) 이므로 무한 반복을 하게 된다.


쓰레드를 즉시 종료시키기 위해서 stop() 메소드를 제공하고 있는데, 이는 쓰지 않는다(deprecated 됨).
stop() 메소드는 쓰레드가 사용 중이던 자원들이 불완전한 상태로 남겨지기 때문이다.


안전하게 Thread를 종료시키는 방법은

boolean 변수를 사용하는 방법과 interrupted() 메소드를 이용하는 방법이 있다.



interrupted() 메소드를 이용하는 방법 (권장)

interrupt() 메소드는 일시 정지 상태일 때 정지가 된다.
Thread.sleep(1); // 일시 정지 상태일 경우 interruptedException을 발생시킨다.
실행대기 또는 실행상태에서는 interruptedException이 발생하지 않는다.
일시 정지 상태를 만들지 않고 while문을 빠져나오는 방법 즉 쓰레드를 종료시키는 방법이 있다.
Thread.interrupted() 메소드를 이용하면 된다.

public class StopThread extends Thread {

    public void run() {
        while(true) {
            System.out.println("실행 중");
            if(Thread.interrupted()) {
                break;
            }
        }
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
}

public class ThreadStopExample {

    public static void main(String[] args) {
        Thread thread = new StopThread();
        thread.start();
       
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       
        thread.interrupt();
    }
}

1초 뒤에 호출되는 thread.interrupt()에 의해 무한 루프는 종료된다.

하지만 더 깔끔하게 처리하는 방법은 StopThread 의 while 문을 아래와 같이 하는 것이다.

public class StopThread extends Thread {

    public void run() {
        while(!Thread.currentThread().isInterrupted()) {
            System.out.println("실행 중");
        }
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
}



boolean 변수를 사용하는 방법

public class StopThread extends Thread {
    private boolean flag = false;
   
    StopThread() {
        this.flag = true; // 생성자에 flag 설정
    }
   
    public void Action(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        while(flag) {
            System.out.println("실행 중");
        }
        System.out.println("자원 정리");
        System.out.println("실행 종료");
    }
}

public class ThreadStopExample {

    public static void main(String[] args) {
        Thread thread = new StopThread();
        thread.start();
       
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       
        ((StopThread) thread).Action(false);
    }
}


블로그 이미지

Link2Me

,
728x90

실행중인 하나의 프로그램을 프로세스(Process)라고 한다.

프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)를 할당받아 프로세스가 된다.

프로세스는 프로그램을 수행하는 데 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로 구성되어 있으며, 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 바로 쓰레드이다.

   - 하나의 프로세스 내부에서 독립적으로 실행되는 하나의 작업 단위를 Thread(쓰레드)라고 한다.

   - 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재하며,

   - 둘 이상의 쓰레드를 가진 프로세스를 멀티 프로세스라고 한다.
     메신저로 채팅을 하면서 파일을 다운로드 받을 수 있는 것은 멀티 쓰레드로 작성되었기 때문이다.

 

Java에서 쓰레드를 구현하는 방법은 두가지가 있다.

public class ThreadBasic {
    public static void main(String[] args) {
        // 1. Thread Class를 상속받는 방법
        Thread t1 = new MyThread1(); // 쓰레드의 생성
        t1.start(); // 쓰레드의 실행. run()이 종료되면 MyThread1 소멸된다.
 
        // 2. Runnable 인터페이스를 구현하는 방법
        Thread t2 = new Thread(new MyThread2());
        t2.start(); // 쓰레드의 실행. run()이 종료되면 MyThread2 소멸된다.
 
    }
}
 
// 1. Thread Class를 상속받는 방법
class MyThread1 extends Thread {
    @Override
    public void run() {
        //작업할 내용
    }
}
 
// 2. Runnable 인터페이스를 구현하는 방법
class MyThread2 implements Runnable {
    @Override
    public void run() {
        //작업할 내용
    }
}
 

 

 

Thread Class를 상속받으면 다른 클래스를 상속 받을 수 없기때문에,

Runnable 인터페이스를 구현하는게 일반적인 방법이다.

Runnable 인터페이스는 run()만 정의되어 있는 간단한 인터페이스이다.

 

Runnable 인터페이스를 구현한 경우, Runnable 인터페이스를 구현한 클래스의 인스턴스를 생성한 다음, 이 인스턴스를 Thread 클래스의 생성자의 매개변수로 제공해야 한다.

 

쓰레드를 생성했다고 해서 자동으로 실행되는 것은 아니다. start() 메소드를 호출해야만 쓰레드가 실행된다.

쓰레드 동작시 run()이 자동적으로 호출된다.

main 메소드의 작업을 수행하는 것도 쓰레드이며, 이를 main 쓰레드라고 한다.

 

 

 

작업할 내용에는 무한 루프 while(flag) { } 를 사용하여 쓰레드의 동작을 제어하도록 한다.

thread를 중단시킬 수 있는 키 값(flag)을 꼭 세팅하여 메인 쓰레드가 종료될 때 같이 종료되도록 하여야 한다.

 

예제1) 쓰레드 기본 동작 확인

public class ThreadEX1 {
    static long startTime = 0;
    public static void main(String[] args) { // ==> main 메소드의 코드를 수행하는 쓰레드
        // 모든 프로세스(실행중인 프로그램)는 최소한 하나의 쓰레드(프로세스 내에서 실제 작업을 수행)를 갖고 있다.
        // 대부분의 프로그램이 멀티쓰레드로 작성되어 있다.
        // 멀티쓰레드 장점 : 1. 시스템 자원의 효율적 사용
        // 2. 사용자에 대한 응답성 향상(채팅 중에 파일 전송)
        // 3. 작업이 분리되어 코드가 간결해진다.
        // 멀티쓰레드 단점 : 1. 동기화에 주의해야 한다. 2. 교착상태가 발생하지 않도록 해야 한다.
        // 3. 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야 한다.
 
        // 쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다.
        ThreadEx1_A t1 = new ThreadEx1_A(); // 쓰레드의 생성
 
        Runnable r = new ThreadEx1_B();
        Thread t2 = new Thread(r);
 
        t1.start(); // 쓰레드 t1을 실행시킨다. ==> 새로운 호출스택 생성
        t2.start(); // 쓰레드 t2를 실행시킨다. ==> 새로운 호출스택 생성
        // 쓰레드는 순차적으로 실행되지 않고 섞여서 실행된다.
        // 실행순서는 OS 스케줄러가 결정한다.
        // 쓰레드는 '사용자 쓰레드' 와 '데몬 쓰레드' 두 종류가 있다.
        // 실행중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
 
        startTime = System.currentTimeMillis();
        try {
            t1.join();    // main쓰레드가 t1의 작업이 끝날 때까지 기다린다.
            t2.join();    // main쓰레드가 t2의 작업이 끝날 때까지 기다린다.
        } catch(InterruptedException e) {}
 
        System.out.print("소요시간:" + (System.currentTimeMillis() - ThreadEX1.startTime));
    }
}
 
class ThreadEx1_A extends Thread { // 1. 쓰레드 Class 상속
    public void run() { // Thread 클래스의 run()을 오버라이딩
        for(int i=0; i < 300; i++) {
            //System.out.println(getName()); // 조상인 Thread의 getName()을 호출
            System.out.print(new String("-"));
        }
    }
}
 
class ThreadEx1_B implements Runnable { // 2. Runnable  인터페이스 구현
    public void run() { // Runnable 인터페이스의 추상메소드 run()을 구현
        // run() : 쓰레드가 수행할 작업을 작성
        for(int i=0; i < 300; i++) {
            // Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
            //System.out.println(Thread.currentThread().getName());
            System.out.print(new String("|"));
        }
    }
}

 

예제2) 싱글 쓰레드

public class SingleThread {
    public static void main(String[] args) {
        // main 쓰레드 내에서 for 문이 순차적으로 실행되는 걸 확인할 수 있다.
        long startTime = System.currentTimeMillis();
 
        for(int i=0; i < 300; i++)
            System.out.printf("%s"new String("-"));
 
        System.out.print("소요시간1:" +(System.currentTimeMillis()- startTime));
 
        System.out.println(); // 줄바꿈 표시 목적
 
        for(int i=0; i < 300; i++)
            System.out.printf("%s"new String("|"));
 
        System.out.print("소요시간2:"+(System.currentTimeMillis() - startTime));
    }
}
 

 

예제3) 멀티쓰레드

public class MultiThread {
    static long startTime = 0;
    public static void main(String[] args) {
        // 실행할 때마다 출력되는 두 작업의 결과가 섞이는 걸 확인할 수 있다.
        // 두개의 작업이 번갈아 가면서 실행될 때 context switching이 발생하여
        // 멀티쓰레드시 작업시간이 싱글쓰레드보다 약간 더 소요된다.
        // 하지만 동시 작업 수행이 가능하다(채팅 중에서 파일 다운로드)
        ThreadEx_C t1 = new ThreadEx_C();
        t1.start();
        startTime = System.currentTimeMillis();
 
        for(int i=0; i < 300; i++)
            System.out.printf("%s"new String("-"));
 
        System.out.print("소요시간1:" + (System.currentTimeMillis() - MultiThread.startTime));
    }
}
 
class ThreadEx_C extends Thread {
    public void run() {
        for(int i=0; i < 300; i++)
            System.out.printf("%s"new String("|"));
 
        System.out.print("소요시간2:" + (System.currentTimeMillis() - MultiThread.startTime));
    }
}
 

 

 

main thread
- 모든 자바 프로그램은 메인 쓰레드가 main() 메소드를 실행하면서 시작한다.
- return 을 만나면 실행을 종료한다.
- main 쓰레드는 작업 쓰레드를 만들어서 병렬로 코드를 실행할 수 있다.
  작업 쓰레드를 만들려면, Thread 클래스를 상속하거나 Runnable 인터페이스를 구현한다.
- 멀티 쓰레드는 실행중인 쓰레드가 하나라도 있다면, 프로세스는 종료되지 않는다.
- 멀티 쓰레드 작업 시에는 각 쓰레드 끼리 정보를 주고받을 수 있어 처리 과정의 오류를 줄일 수 있다.
- 프로세스끼리는 정보를 주고 받을 수 없다.


 

데몬(daemon) 쓰레드

- 일반 쓰레드(non-daemon thread)의 작업을 돕는 보조적인 역할을 수행한다.
- 일반 쓰레드가 모두 종료되면 자동적으로 종료된다.
- 가비지 컬렉터, 자동저장, 화면자동갱신 등에 사용된다.
- 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.

- setDaemon(boolean on)은 반드시 start()를 호출하기 전에 실행되어야 한다.

 

 

public class DaemonThreadEx implements Runnable {
    static boolean autoSave = false;
 
    public static void main(String[] args) {
        Thread t = new Thread(new DaemonThreadEx()); // Thread(Runnable r)
        // run() 메소드를 가지고 있는 클래스의 객체를 매개변수로 준다.
        t.setDaemon(true); // 이 부분이 없으면 종료되지 않는다.
        t.start();
 
        for(int i=1; i <= 20; i++) {
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
 
            }
            System.out.println(i);
 
            if(i == 5) autoSave = true;
        }
        System.out.println("프로그램을 종료합니다.");
    }
 
    public void run() {
        while(true) { // 무한루프
            try {
                Thread.sleep(3 * 1000); // 3초 마다
            } catch(InterruptedException e) {
 
            }
 
            // autoSava 의 값이 true 이면 autoSave()를 호출한다.
            if(autoSave) autoSave();
        }
    }
 
    private void autoSave() {
        System.out.println("작업 파일이 자동 저장되었습니다.");
    }
}
 
 

 

쓰레드의 생명 주기(Java Thread Life Cycle and Thread States)

 

 

1. New

   - 객체 생성

   - 쓰레드가 만들어진 상태로 아직 start() 메소드가 호출되지 않은 상태다.

 

2. Runnable (실행대기)
   - 쓰레드가 실행되기 위한 준비단계이다.
   - 코드 상에서 start() 메소드를 호출하면 run() 메소드에 설정된 쓰레드가 Runnable 상태로 진입한다.

3. Running (실행상태)
   - CPU를 점유하여 실행하고 있는 상태이며 run() 메서드는 JVM만이 호출 가능하다.
   - Runnable(준비상태)에 있는 여러 쓰레드 중 우선 순위를 가진 쓰레드가 결정되면

   - JVM이 자동으로 run() 메소드를 호출하여 쓰레드가 Running 상태로 진입한다.

4. Terminated (실행종료)
   - Running 상태에서 쓰레드가 모두 실행되고 난 후 완료 상태다.

   - run() 메소드 완료시 쓰레드가 종료되며, 그 쓰레드는 다시 시작할 수 없게 된다.

 

5. Blocked (일시 정지)
   - 사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태다.
   - wait() 메소드에 의해 Blocked 상태가 된 쓰레드는 notify() 메소드가 호출되면 Runnable 상태로 간다.
   - sleep(시간) 메소드에 의해 Blocked 상태가 된 쓰레드는 지정된 시간이 지나면 Runnable 상태로 간다.

 

join() 메소드는 다른 쓰레드의 종료를 기다리는 것이다.

계산 작업을 하는 쓰레드가 모든 계산 작업을 마쳤을 때, 계산 결과값을 받아 이용하는 경우에 주로 사용한다.

 

 
public class JoinMain {
    public static void main(String[] args) {
        JoinWorkerThread thread = new JoinWorkerThread();
        thread.start(); // 작업 쓰레드 시작
 
        System.out.println("Thread가 종료될때까지 기다린다.");
        try {
            // 별도의 쓰레드에서 처리한 작업이 완료될 때까지 기다려야 하는 경우
            thread.join(); // 해당 작업 쓰레드가 종료될 때까지 main 쓰레드가 대기한다.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread가 종료되었다.");
    }
}
 
class JoinWorkerThread extends Thread {
 
    @Override
    public void run() {
        for(int i = 0; i < 5; i++){
            System.out.println("JoinWorkerThread : "+ i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}

 

실행 결과

Thread가 종료될때까지 기다린다.
JoinWorkerThread : 0
JoinWorkerThread : 1
JoinWorkerThread : 2
JoinWorkerThread : 3
JoinWorkerThread : 4
Thread가 종료되었다.

 

Android 어플을 GitHub 에서 받아서 테스트 해보니 제대로 동작이 잘 안되어 수정해보려고 했으나 쓰레드에 대한 이해가 부족하여 쓰레드 자료/강좌를 찾아서 공부하고 있다.

"이것이 자바다" 유투브 동영상 강좌(https://www.youtube.com/watch?v=1mut1RwpEvw)에 설명이 잘 되어 있다.

Do IT 안드로이드 앱 프로그래밍 책 저자의 동영상을 들었을 때, 자바를 몰라도 안드로이드 어플 만들 수 있다고 한다.

물론 간단한 앱 테스트하는 것은 얼마든지 만들 수 있다.

하지만 응용을 해야 한다거나, 난이도 있는 어플을 만들려고 하면 그 분야의 지식을 깊게 만들어야 어플이 문제없이 동작된다는 걸 절감하게 된다.

블로그 이미지

Link2Me

,
728x90

"이것이 자바다" 유투브 동영상 강좌(https://www.youtube.com/watch?v=twd6mwUS1Bc)에 나오는 코드를 적고 테스트해보고 기록해둔다.

Socket을 활용하여 Clinet측에서 Server로 일대일 연결을 유지하면서 Client측에서 보낸 메세지를 Server측에서 수신하여 수신 받은 메세지를 다시 Client측으로 송신하는 프로그램 구현이다.




서버 소켓 코드

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            // 소켓 생성
            serverSocket = new ServerSocket();
            // 포트 바인딩
            serverSocket.bind(new InetSocketAddress("localhost", 5001));

            while (true) {
                System.out.println("[연결 기다림]");
                // 연결 수락
                socket = serverSocket.accept(); // 클라이언트가 접속해 오기를 기다리고, 접속이 되면 통신용 socket 을 리턴한다.
                // 연결된 클라이언트 IP 주소 얻기
                InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
                System.out.println("[연결 수락함] " + isa.getHostName());

                byte[] bytes = null;
                String message = null;

                // 데이터 받기
                InputStream is = socket.getInputStream();
                bytes = new byte[100];
                int readByteCount = is.read(bytes);
                message = new String(bytes, 0, readByteCount, "UTF-8");
                System.out.println("[데이터 받기 성공] " + message);

                // 데이터 보내기
                OutputStream os = socket.getOutputStream();
                message = "Hello Client";
                bytes = message.getBytes("UTF-8");
                os.write(bytes);
                os.flush();
                System.out.println("[데이터 보내기 성공]");

                is.close();
                os.close();
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (!serverSocket.isClosed()) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}



클라이언트 코드

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

public class ChatClient {

    public static void main(String[] args) {

        Socket socket = null;

        try {
            socket = new Socket();
            System.out.println("[연결 요청]");
            socket.connect(new InetSocketAddress("localhost", 5001));
            System.out.println("[연결 성공]");
           
            byte[] bytes = null;
            String message = null;

            OutputStream os = socket.getOutputStream();
            message = "Hello Server, I'm Client.";
            bytes = message.getBytes("UTF-8");
            os.write(bytes);
            os.flush();
            System.out.println("[데이터 보내기 성공]");
           
            InputStream is = socket.getInputStream();
            bytes = new byte[100];
            int readByteCount = is.read(bytes);
            message = new String(bytes,0,readByteCount,"UTF-8");
            System.out.println("[데이터 받기 성공] " + message);
           
            os.close();
            is.close();
           
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        if (!socket.isClosed()) {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}


코드를 작성하고 실행하면 콘솔에 나오는 출력 결과를 확인하고 알 수 있다.


GitHub 에 간단하면서도 좋은 예제가 있다.

https://gist.github.com/junsuk5/f0ff2298e17853dc48e89f2dfc7bd985

블로그 이미지

Link2Me

,
728x90

HashMap ketSet<String> 을 String 배열로 만들는 법을 적어둔다.

String[] mKeys = map.keySet().toArray(new String[map.size()]);

를 활용할 기회가 있었다.

key 로부터 map.get(key); 로 값을 얻을 수 있다.


key 를 배열로 만들어야 value를 얻을 수 있을 때 아래 예제를 살펴보면 도움이 될 것이다.


import java.util.HashMap;
import java.util.Map;

public class HashMap_Test {

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("1", "대조영");
        map.put("2", "이순신");
        map.put("4", "강감찬");
        map.put("3", "양만춘");
        map.put("5", "을지문덕");
        map.put("3", "연개소문"); // HashMap 은 key 가 같고 값이 다르면 value를 덮어쓰기를 한다.
       
        // Getting the value of "4" key.
        System.out.println("The Value is: " + map.get("4"));

        map.containsKey("강아지"); // key 가 존재하면 true 반환
        map.containsValue("조나비"); // value 가 존재하면 true 반환

        // Using keySet() to get the set view of keys
        System.out.println("The set is: " + map.keySet());
       
        String[] mKeys = map.keySet().toArray(new String[map.size()]);
        for (String key : mKeys) {
            System.out.println(key);
        }
       
        for(int i=0; i < mKeys.length;i++) {
            System.out.println("key :" + mKeys[i] + ", value : " + map.get(mKeys[i]));
        }
    }

}
 


HashMap의 경우 단점이 put을 통해 데이터나 객체를 넣을때 key의 순서가 지켜지지 않는다는 것이다.
개발을 할때 코드상으로 순차적으로 key/value를 넣어도, 실제 HashMap에서는 해당 순서가 지켜지지 않는다.
만약 입력된 Key의 순서가 보장되어야 한다면 LinkedHashMap을 사용하면 된다.

LinkedHashMap 은 기본적으로 HashMap을 상속받아 만들어져 있게 때문에 HashMap의 기능을 그대로 사용 가능하다.
대신 여기에 순서라는 개념이 들어 갔다.



참고하면 도움이 될 자료

https://stackoverflow.com/questions/5234576/what-adapter-shall-i-use-to-use-hashmap-in-a-listview


블로그 이미지

Link2Me

,
728x90

Hashmap 을 ArrayList 로 변환하는 방법에 대한 코드 예제가 있어서 약간 추가해서 테스트해보고 적어둔다.

HashMap 은 Map 인터페이스의 한 종류로 key 와 value 를 묶어서 하나의 entry로 저장한다는 특징을 가지고 있다.

key 값은 중복이 불가능하고 value 값은 중복이 가능하며, null 값도 사용 가능하다.

해싱 기법으로 데이터를 저장하고, 데이터가 많아도 검색이 빠르다.

멀티쓰레드에서 동시에 HashMap key - value 를 사용하면 문제가 될 수 있다.

멀티쓰레드에서는 HashTable을 쓴다.


HashMap 을 ArrayList 로 변환하는 3가지 방법이 있다.

HashMap key 를 ArrayList 로 변환, HashMap value 를 ArrayList 로 변환, HashMap key-value 를 ArrayList 로 변환할 수 있다.

Iterator 인터페이스를 사용할 수 없는 컬렉션인 Map 에서 Iterator 인터페이스를 사용하기 위해서는 Map 에 keySet(), entrySet() 메소드를 사용하여 Set 객체를 반환한 후 Iterator 인터페이스를 사용하면 된다.


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

public class HashMapToArrayList {

    public static void main(String[] args) {
        //Creating a HashMap object
        HashMap<String, String> map = new HashMap<String, String>();

        //Adding elements to HashMap
        map.put("1", "이순신");
        map.put("2", "강감찬");
        map.put("5", "주몽");
        map.put("6", "대조영");
        map.put("3", "을지문덕");
        map.put("4", "연개소문");

        // Getting Set of keys
        Set<String> keySet = map.keySet();

        // ArrayList 의 key 생성
        ArrayList<String> listOfKeys = new ArrayList<String>(keySet);
        System.out.println("ArrayList Of Keys :");
        for (String key : listOfKeys) {
            System.out.println(key);
        }
        System.out.println("--------------------------");

        // Getting Collection of values
        Collection<String> values = map.values();

        // ArrayList 의 값 생성
        ArrayList<String> listOfValues = new ArrayList<String>(values);
        System.out.println("ArrayList Of Values :");
        for (String value : listOfValues) {
            System.out.println(value);
        }
        System.out.println("--------------------------");

        //Getting the Set of entries
        Set<Entry<String, String>> entrySet = map.entrySet();

        // ArrayList 의  Entry objects 생성
        ArrayList<Entry<String, String>> listOfEntry = new ArrayList<Entry<String,String>>(entrySet);
        System.out.println("ArrayList of Key-Values :");
        for (Entry<String, String> entry : listOfEntry) {
            System.out.println(entry.getKey()+" : "+entry.getValue());
        }


        // entrySet().iterator()
        Iterator<Map.Entry<String,String>> iteratorE = map.entrySet().iterator();
        System.out.println("------- Iterator<Map.Entry<String,String>> -----------");
        while(iteratorE.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) iteratorE.next();
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key : " + key + " , value : "+value);
        }
       
        // 방법 05 : keySet().iterator()
        Iterator<String> iteratorK = map.keySet().iterator();
        System.out.println("------- Iterator<String> iteratorK -----------------");
        while (iteratorK.hasNext()) {
            String key = iteratorK.next();
            String value = map.get(key);
            System.out.println("key : " + key + ", value : " + value);
        }

    }
}


참고하면 도움이 될 자료

ArrayList and HashMap : https://link2me.tistory.com/1210

https://javaconceptoftheday.com/convert-hashmap-to-arraylist-in-java/


Map 동영상 강좌

https://programmers.co.kr/learn/courses/9/lessons/260#

Java의 정석, 11. 컬렉션 프레임웍 : https://www.youtube.com/watch?v=y3qLfTOmONE


블로그 이미지

Link2Me

,
728x90

달력을 구현하다보니 September 와 같은 영문 월에 대한 입력을 받아서 숫자 월을 반환하는 코드가 필요하다.

양력 → 음력으로 날짜를 반환할 목적이라서 숫자 대신에 숫자 문자열로 반환하도록 처리했다.


private static String ConvertMonthString(String month){
    // September 와 같은 입력을 9와 같은 숫자 문자열로 반환
    Date date = null;
    try {
        date = new SimpleDateFormat("MMM", Locale.ENGLISH).parse(month);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return String.valueOf(cal.get(Calendar.MONTH));
    } catch (ParseException e) {
        e.printStackTrace();
        return "";
    }
}


블로그 이미지

Link2Me

,
728x90

ArrayList 를 리턴 받는 함수를 만드는 방법이다.


import java.util.ArrayList;

public class Test {
    public ArrayList<Integer> myNumbers() {
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        numbers.add(3);
        numbers.add(5);
        numbers.add(8);
        numbers.add(11);
        return (numbers);

    }
}


public class Main {

    public static void main(String[] args) {
        Test test = new Test();
        ArrayList<Integer> arr = test.myNumbers();
    }

}

'안드로이드 > Java 문법' 카테고리의 다른 글

Convert HashMap To ArrayList  (0) 2019.10.21
[Java] Convert Month String to Integer  (0) 2019.10.14
자바 null 비교, String 비교, 정수 비교  (0) 2019.09.20
공백으로 문자열 분리  (0) 2019.09.11
Java 제네릭(Generic)  (0) 2019.08.27
블로그 이미지

Link2Me

,