Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions src/main/java/calculator/App.java

This file was deleted.

3 changes: 0 additions & 3 deletions src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package lotto;

import lotto.InputView;
import lotto.ResultView;

public class Application {
public static void main(String[] args) {
LottoMachine machine = new LottoMachine(new ShuffleNumberGenerator());
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/lotto/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ public List<Integer> readWinningNumbers() {
.map(Integer::parseInt)
.collect(Collectors.toList());
}

public int readBonusNumber() {
System.out.println("보너스 볼을 입력해 주세요.");
return Integer.parseInt(scanner.nextLine().trim());
}
}
67 changes: 28 additions & 39 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,66 +1,55 @@
package lotto;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Lotto {
private static final int SIZE = 6;
private static final int MIN = 1;
private static final int MAX = 45;
private static final int LOTTO_SIZE = 6;

private final Set<Integer> numbers;
private final Set<LottoNumber> numbers;

public Lotto(List<Integer> numbers) {
validateSize(numbers);
validateRange(numbers);
validateUnique(numbers);
this.numbers = Set.copyOf(numbers);
}

public Lotto(int... numbers) {
this(Arrays.stream(numbers).boxed().collect(Collectors.toList()));
}

public List<Integer> numbers() {
return numbers.stream()
.sorted()
.collect(Collectors.toList());
}

public boolean contains(int number) {
return numbers.contains(number);
this(toLottoNumbers(numbers));
}

public int matchCount(Lotto winning) {
return (int) numbers.stream()
.filter(winning::contains)
.count();
private Lotto(Set<LottoNumber> numbers) {
validate(numbers);
this.numbers = Set.copyOf(numbers);
}

private void validateSize(List<Integer> numbers) {
if (numbers.size() != SIZE) {
throw new IllegalArgumentException("로또 번호는 6개여야 한다.");
private static Set<LottoNumber> toLottoNumbers(List<Integer> numbers) {
Set<LottoNumber> result = new HashSet<>();
for (int n : numbers) {
result.add(LottoNumber.from(n));
}
return result;
}

private void validateRange(List<Integer> numbers) {
if (numbers.stream().anyMatch(this::isOutOfRange)) {
throw new IllegalArgumentException("로또 번호는 1~45 범위여야 한다.");
private void validate(Set<LottoNumber> numbers) {
if (numbers.size() != LOTTO_SIZE) {
throw new IllegalArgumentException("로또 번호는 중복 없이 " + LOTTO_SIZE + "개여야 합니다.");
}
}

private boolean isOutOfRange(int number) {
return number < MIN || number > MAX;
public boolean contains(LottoNumber number) {
return numbers.contains(number);
}

private void validateUnique(List<Integer> numbers) {
if (new HashSet<>(numbers).size() != SIZE) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없다.");
public int matchCount(Lotto other) {
int count = 0;
for (LottoNumber n : numbers) {
if (other.contains(n)) count++;
}
return count;
}

public List<Integer> numbers() {
return numbers.stream()
.map(LottoNumber::value)
.sorted()
.collect(Collectors.toList());
}
}
12 changes: 5 additions & 7 deletions src/main/java/lotto/LottoController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package lotto;

import lotto.InputView;
import lotto.ResultView;

import java.util.List;

public class LottoController {
Expand All @@ -18,14 +17,13 @@ public LottoController(InputView inputView, ResultView resultView, LottoMachine
public void run() {
Money money = Money.of(inputView.readPurchaseAmount());

List<Lotto> lottos = lottoMachine.issue(money);
resultView.printLottos(lottos);
Lottos tickets = lottoMachine.issue(money);
resultView.printLottos(tickets);

Lotto winning = new Lotto(inputView.readWinningNumbers());
WinningNumbers winningNumbers = new WinningNumbers(winning, inputView.readBonusNumber());

WinningStatistics stats = new WinningStatistics();
stats.accumulate(lottos, winning);

WinningStatistics stats = winningNumbers.match(tickets);
resultView.printStatistics(stats, money);
}
}
4 changes: 2 additions & 2 deletions src/main/java/lotto/LottoMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ public LottoMachine(LottoNumberGenerator generator) {
this.generator = generator;
}

public List<Lotto> issue(Money money) {
public Lottos issue(Money money) {
List<Lotto> lottos = new ArrayList<>();
for (int i = 0; i < money.ticketCount(); i++) {
lottos.add(new Lotto(generator.generate()));
}
return List.copyOf(lottos);
return new Lottos(lottos);
}
}
50 changes: 50 additions & 0 deletions src/main/java/lotto/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package lotto;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class LottoNumber {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상황

  • 1만명의 사용자가 동시에 당첨 여부를 확인할 수 있어야 한다.
  • 1명의 사용자는 평균 5장의 로또를 구매한 상태이다.

위 요구사항을 서버에서 생성되는 LottoNumber의 인스턴스의 갯수는?
1 * 5장 * 6개 숫자/1장 * 1만명 = 30만개이다.

동시에 생성되는 인스턴스 갯수가 너무 많다.
인스턴스 갯수를 줄일 수 있는 방법은?
로또 숫자 값을 LottoNumber 객체로 래핑하는 경우 매번 인스턴스가 생성되기 때문에 인스턴스의 갯수가 너무 많아져 성능이 떨어질 수 있다.
LottoNumber 인스턴스를 생성한 후 재사용할 수 있도록 구현한다.

힌트 : Map과 같은 곳에 인스턴스를 생성한 후 재사용하는 방법을 찾아본다.

private static final int MIN = 1;
private static final int MAX = 45;

private static final Map<Integer, LottoNumber> CACHE = new ConcurrentHashMap<>();

private final int value;

private LottoNumber(int value) {
this.value = value;
}

public static LottoNumber from(int value) {
validateRange(value);
return CACHE.computeIfAbsent(value, LottoNumber::new);
}

private static void validateRange(int value) {
if (value < MIN || value > MAX) {
throw new IllegalArgumentException("로또 번호는 " + MIN + " ~ " + MAX + " 사이여야 합니다. value=" + value);
}
}

public int value() {
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LottoNumber)) return false;
return value == ((LottoNumber) o).value;
}

@Override
public int hashCode() {
return Integer.hashCode(value);
}

@Override
public String toString() {
return String.valueOf(value);
}
}
21 changes: 21 additions & 0 deletions src/main/java/lotto/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package lotto;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Lottos {
private final List<Lotto> values;

public Lottos(List<Lotto> values) {
this.values = List.copyOf(values);
}

public List<Lotto> values() {
return values;
}

public int size() {
return values.size();
}
}
19 changes: 19 additions & 0 deletions src/main/java/lotto/MatchResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package lotto;

public class MatchResult {
private final int matchCount;
private final boolean bonusMatched;

public MatchResult(int matchCount, boolean bonusMatched) {
this.matchCount = matchCount;
this.bonusMatched = bonusMatched;
}

public int matchCount() {
return matchCount;
}

public boolean bonusMatched() {
return bonusMatched;
}
}
50 changes: 27 additions & 23 deletions src/main/java/lotto/Rank.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,54 @@
package lotto;

import java.util.Arrays;

import lotto.MatchResult;

public enum Rank {
FIRST(6, 2_000_000_000, "6개 일치"),
THIRD(5, 1_500_000, "5개 일치"),
FOURTH(4, 50_000, "4개 일치"),
FIFTH(3, 5_000, "3개 일치"),
MISS(0, 0, "");
FIRST(6, false, 2_000_000_000, "6개 일치", 5),
SECOND(5, true, 30_000_000, "5개 일치, 보너스 볼 일치", 4),
THIRD(5, false, 1_500_000, "5개 일치", 3),
FOURTH(4, false, 50_000, "4개 일치", 2),
FIFTH(3, false, 5_000, "3개 일치", 1),
MISS(0, false, 0, "", 0);

private final int matchCount;
private final long prize;
private final boolean bonusNeeded;
private final int prize;
private final String description;
private final int displayOrder;

Rank(int matchCount, long prize, String description) {
Rank(int matchCount, boolean bonusNeeded, int prize, String description, int displayOrder) {
this.matchCount = matchCount;
this.bonusNeeded = bonusNeeded;
this.prize = prize;
this.description = description;
this.displayOrder = displayOrder;
}

public int matchCount() {
return matchCount;
public static Rank of(MatchResult result) {
int matchCount = result.matchCount();
boolean bonusMatched = result.bonusMatched();

if (matchCount == 6) return FIRST;
if (matchCount == 5) return bonusMatched ? SECOND : THIRD;
if (matchCount == 4) return FOURTH;
if (matchCount == 3) return FIFTH;
return MISS;
}

public long prize() {
public int prize() {
return prize;
}

public String description() {
return description;
}

public boolean isWinning() {
return this != MISS;
}

public static Rank of(int matchCount) {
return Arrays.stream(values())
.filter(rank -> rank.matchCount == matchCount)
.findFirst()
.orElse(MISS);
public int displayOrder() {
return displayOrder;
}

public static Rank[] winningRanks() {
return Arrays.stream(values())
.filter(Rank::isWinning)
.toArray(Rank[]::new);
return new Rank[]{FIFTH, FOURTH, THIRD, SECOND, FIRST};
}
}
17 changes: 11 additions & 6 deletions src/main/java/lotto/ResultView.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package lotto;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class ResultView {
public void printLottos(List<Lotto> lottos) {
public void printLottos(Lottos lottos) {
System.out.println(lottos.size() + "개를 구매했습니다.");
lottos.forEach(lotto -> System.out.println(lotto.numbers()));
lottos.values().forEach(lotto -> System.out.println(lotto.numbers()));
}

public void printStatistics(WinningStatistics stats, Money money) {
System.out.println();
System.out.println("당첨 통계");
System.out.println("---------");

for (Rank rank : Rank.winningRanks()) {
System.out.println(rank.description() + " (" + rank.prize() + "원)- "
+ stats.countOf(rank) + "개");
}
Arrays.stream(Rank.winningRanks())
.sorted(Comparator.comparingInt(Rank::displayOrder))
.forEach(rank -> printRank(stats, rank));

System.out.println("총 수익률은 " + stats.profitRate(money) + "입니다.");
}

private void printRank(WinningStatistics stats, Rank rank) {
System.out.println(rank.description() + " (" + rank.prize() + "원)- " + stats.countOf(rank) + "개");
}
}
Loading