// 42-1 익명 클래스의 인스턴스를 함수 객체로 사용 - 낡은 기법이다! (254쪽)
Collections.sort(words, new Comparator<String>() {
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
public static void main(String[] args) {
Item42 item = new Item42();
item.exam01();
}
// 42-2 람다식을 함수 객체로 사용 - 익명 클래스 대체 (255쪽)
Collections.sort(words, (s1,s2)-> Integer.compare(s1.length(), s2.length()));
Collections.sort(words, comparingInt(String::length));
words.sort(comparingInt(String::length));
public void exam02(int version){
switch (version){
case 1 : Collections.sort(words, (s1,s2)-> Integer.compare(s1.length(), s2.length())); break;
case 2 : Collections.sort(words, Comparator.comparingInt(String::length)); break;
case 3 : words.sort(Comparator.comparingInt(String::length)); break;
}
System.out.println("[42-exam02] "+words);
}
public static void main(String[] args) {
Item42 item = new Item42();
item.exam02(1);
}
// 42-3 상수별 클래스 몸체와 데이터를 사용한 열거 타입(코드 34-6)
public enum Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
@Override public String toString() { return symbol; }
public abstract double apply(double x, double y);
}
// 42-4 함수 객체(람다)를 인스턴스 필드에 저장해 상수별 동작을 구현한 열거 타입
public enum Operation {
PLUS ("+", (x, y) -> x + y),
MINUS ("-", (x, y) -> x - y),
TIMES ("*", (x, y) -> x * y),
DIVIDE("/", (x, y) -> x / y);
private final String symbol;
private final DoubleBinaryOperator op;
Operation(String symbol, DoubleBinaryOperator op) {
this.symbol = symbol;
this.op = op;
}
@Override public String toString() { return symbol; }
public double apply(double x, double y) {
return op.applyAsDouble(x, y);
}
}
public static void main(String[] args) {
double x = 10;
double y = 2;
for (Operation op : Operation.values()) {
System.out.printf("%f %s %f = %f\\n", x, op.toString(), y, op.apply(x, y));
}
}
// DoubleBinaryOperator - 함수 인터페이스중 하나로,
// Double 타입 인수 2개를 받아 Double 타입 결과를 돌려준다
@FunctionalInterface
public interface DoubleBinaryOperator {
double applyAsDouble(double left, double right);
}
// 44-1 불필요한 함수형 인터페이스 - 대신 표준 함수형 인터페이스를 사용하라.
@FunctionalInterface
interface EldestEntryRemovalFunction<K,V> {
boolean remove(Map<K,V> map, Map.Entry<K,V> eldest);
}
// Comparator
@FunctionInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
// ToIntBiFunction
@FunctionalInterface
public interface ToIntBiFunction<T, U> {
int applyAsInt(T t, U u);
}
public interface ExecutorService extends Executor {
// Callable<T>와 Runnable을 각각 인수로 하여 다중정의했다.
// submit 메서드를 사용할 때마다 형변환이 필요해진다.
<T> Future<T> submit(Callback<T> task);
Future<?> submit(Runnable task);
}
45. 스트림은 주의해서 사용하라
// 45-1 사전 하나를 훑어 원소 수가 많은 아나그램 그룹들을 출력한다.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;
import static java.util.stream.Collectors.groupingBy;
public class Anagrams {
public static void main(String[] args) throws IOException {
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);
}
}
// 45-2 스트림을 과하게 사용했다. - 따라 하지 말 것!
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;
import static java.util.stream.Collectors.groupingBy;
public class Anagrams {
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 -> word.chars().sorted()
.collect(StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append).toString()))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.map(group -> group.size() + ": " + group)
.forEach(System.out::println);
}
}
}
// 45-4 데카르트 곱 계산을 반복 방식으로 구현
private static List<Car> newDeck() {
List<Card> result = new ArrayList<>();
for (Suit suit : Suit.values())
for (Rank rank : Rank.values())
result.add(new Card(suit, rank));
return result;
}
// 45-5 데카르트 곱 계산을 스트림 방식으로 구현
private static List<Car> newDeck() {
return Stream.of(Suit.values())
.flatMap(Suit->
Stream.of(Rank.values())
.map(**rank-> new Card(suit, rank)**))
.collect(toList());
}
46. 스트림에서는 부작용 없는 함수를 사용하라
// 46-1 스트림 패러다임을 이해하지 못한 채 API만 사용했다 - 따라 하지 말 것!
Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()){
words.forEach(word -> {
freq.merge(word.toLowerCase(), 1L, Long::sum);
});
}
// 46-2 스트림을 제대로 활용해 빈도표를 초기화한다.
Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()){
freq = words
.collect(groupingBy(String::toLowerCase, counting()))
}
// 46-3 빈도표에서 가장 흔한 단어 10개를 뽑아내는 파이프라인
List<String> topTen = freq.keySet().stream()
.sorted(comparing(freq::get).reversed()) // 빈도수 반환해서 reversed 정렬
.limit(10)
.collect(toList());
// 46-4 toMap 수집기를 사용하여 문자열을 열거 타입 상수에 매핑한다.
private static final Map<String, Operation> stringToEnum =
Stream.of(values()).collect(
**toMap(Object::toString, e -> e)**);
// 46-5 각 키완 해당 키의 특정 원소를 연관 짓는 맵을 생성하는 수집기
Map<Artist, Album> toHits = albums.collect(
toMap(Album::artist, a->a, maxBy(comparing(Album::sales)));
// 46-7 마지막에 쓴 값을 취하는 수집기
toMap(KeyMapper, valueMapper, (oldVal, newVal) -> newVal) // 마지막에 쓴 값을 취하는 수집기
words.collect(groupingBy(word -> alphabetize(word))); // 아나그램 프로그램에서 사용한 수집기이다.
Map<String, Long> freq = words
.collect(groupingBy(String::toLowerCase, counting()));
47. 반환 타입으로는 스트림보다 컬렉션이 낫다
// 47-1 자바 타입 추론의 한계로 컴파일 되지 않는다.
for (ProcessHandle ph : ProcessHandle.allProcesses()::iterator) {
// 프로세스를 처리한다.
}
// 47-2 스트림을 반복하기 위한 '끔찍한' 우회 방법
for (ProcessHandle ph : (Iterable<ProcessHandle>)
ProcessHandle.allProcess()::iterator) { // 프로세스를 처리한다. }
// 47-5 입력 집합의 멱집합을 전용 컬렉션에 담아 반환한다.
public class PowerSet {
public static final <E> Collection<Set<E>> of(Set<E> s) {
List<E> src = new ArrayList<>(s);
if(src.size() > 30) {
throw new IllegalArgumentException("집합에 원소가 너무 많습니다(최대 30개).: " + s);
}
return new AbstractList<Set<E>>() {
@Override
public int size() {
// 멱집합의 크기는 2를 원래 집합의 원소 수만큼 거듭제곱한 것과 같다.
return 1 << src.size();
}
@Override
public boolean contains(Object o) {
return o instanceof Set && src.containsAll((Set) o);
}
@Override
public Set<E> get(int index) {
Set<E> result = new HashSet<>();
for (int i = 0; index != 0; i++, index >>=1) {
if((index & 1) == 1) {
result.add(src.get(i));
}
}
return result;
}
};
}
}