이펙티브자바 정리 6

42. 익명 클래스보다는 람다를 사용하라

  • 타입을 명시해야 코드가 더 명확할 때를 제외하고는 람다의 모든 매개변수 타입은 생략해라
Collections.sort(words, new Comparator<Strin>() {
	public int compare(String s1, String s2) {
		return Integer.compare(s1.length(), s2.length());
	}
});

를 람다로 변환

Collections.sort(words,
		(s1, s2) -> Integer.compare(s1.length(), s2.length()));
  • 람다는 이름이 없고 문서화도 못함으로 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄이 많아지면 쓰지말아야 한다
  • 람다를 직렬화 하지 말아라
  • 익명 클래스는 타입의 인스턴스를 만들 떄만 사용해라

43. 람다보다는 메서드 참조를 사용해라

map.merge(key, 1, (count, incr) -> count + incr);

람다로 변환

map.merge(key, 1, Integer::sum);
  • merge 메서드는 키, 값, 함수를 인자로 받음
  • 키가 맵 안에 없다면 주어진 키, 값을 그대로 쌍으로 저장
  • 키가 존재 한다면 세번째 인수로 받은 함수에 현재 값과 주어진 값을 적용시키고 그 결과값은 저장

메서드 참조 유형

44. 표준 함수형 인터페이스를 사용해라

  • 필요한 용도에 맞다면 직접 구현하지말고 표준함수형 인터페이스를 참조해라
  • 직접 만든 함수형 인터페이스에는 항상 @FunctionalInterface 에너테이션을 사용해라

45. 스트림은 주의해서 사용해라

스트림 API가 제공하는 추상 개념

  1. 스트림 : 무한시퀀스
  2. 스트림 파이프라인 : 원소들로 수행하는 연산 단계 표현
public class Anagrams {
	public static void main(String[] args) throws FileNotFoundException {
		File dictionary = new File(args[0]);
		int minGroupSize = Integer.parseInt(args[1]);

		Map<String, Set<String>> groups = new HashMap<>();
		try (Scanner s = new Scanner(dictionary)) {
			while (s.hasNext()) {
				String word = s.next();
				groups.computeIfAbsent(alphabetize(word),
					(unused) -> new TreeSet<>()).add(word);
			}
		}
		
		for (Set<String>group : groups.values())
			if (group.size() >= minGroupSize)
				System.out.println(group.size() + ": " + group);
	}
	
	private static String alphabetize(String s){
		char[] a = s.toCharArray();
		Arrays.sort(a);
		return new String(a);
	}
}

computeIfAbsent  : 맵 안에 키가 있는지 찾고 있으면 단순 매핑값 반환, 키 없으면 건네진 
함수 객체를 키에 적용하고 값을 계산 후 그 키와 매핑한 후 값을 반환

위 코드를 스트림을 이용하여 변환

public class AnagramStream {
	public static void main(String[] args) throws IOException {
		Path dictionary = Paths.get(args[0]);
		int minGroupSize = Integer.parseInt(args[1]);
		
		try (Stream<String>words = Files.lines(dictionary)){
			words.collect(groupingBy(word -> alphabetize(word)))
				.values().stream()
				.filter(group -> group.size() >= minGroupSize)
				.forEach(g -> System.out.println(g.size() + ": " + g));
				
		}
	}
}

위 코드는 중간 연산은 없고, 종단 연산에서 모든 단어를 수집해 맵으로 모은다

스트림을 사용하면 좋은 경우

  1. 원소들의 시퀀스를 일관되게 반환
  2. 원소들의 시퀀스를 필터링
  3. 원소들의 시퀀스를 하나의 연산을 사용해 결합
  4. 원소들의 시퀀스를 컬렉션에 모음
  5. 원소들의 시퀀스에서 특정 조건을 만족하는 원소를 찾음

46. 스트림에서는 부작용 없는 함수를 사용해라

스트림 패러다임의 핵심 : 계산을 일련의 변환으로 재구성함

→ 각 단계는 이전 단계의 결과를 받아 처리하는 순수함수여야 한다

순수함수 : 오직 입력만이 결과에 영향을 주는 함수

잘못 사용된 스트림

public class wrong {
	Map<String, Long> freq = new HashMap<>();
	try (Stream<String> words = new Scanner(file).tokens()) {
		words.forEach(word -> {
			freq.merge(word.toLowerCase(), 1L, Long::sum);
		});
	}
}
  • forEach로 인한 반복적 코드
  • 모든 종단 작업이 forEach에서 일어남
  • 이때 외부 상태를 수정하는 람다가 (Long::sum)이 실행되면서 문제가 생김

제대로 사용된 스트림

public class Correct {
	Map<String, Long> freq;
	try (Stream<String> words = new Scanner(file).tokens()) {
		freq = words
			.collect(groupingBy(String::toLowerCase, counting()));
	}
}

47. 반환 타입으로는 스트림보다 컬렉션이 낫다

48. 스트림 병렬화는 주의해서 적용해라

좋은 웹페이지 즐겨찾기