diff --git a/src/test/java/part2/exercise/CollectorsExercise1.java b/src/test/java/part2/exercise/CollectorsExercise1.java index 46b6765..0353277 100755 --- a/src/test/java/part2/exercise/CollectorsExercise1.java +++ b/src/test/java/part2/exercise/CollectorsExercise1.java @@ -6,25 +6,49 @@ import org.junit.Test; import java.util.*; -import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.*; +import static org.junit.Assert.assertEquals; public class CollectorsExercise1 { @Test public void getTheCoolestOne() { - final Map coolestByPosition = getCoolestByPosition(getEmployees()); + final Map coolestByPosition1 = getCoolestByPositionFirstOption(getEmployees()); + final Map coolestByPosition2 = getCoolestByPositionSecondOption(getEmployees()); + final Map coolestByPosition3 = getCoolestByPositionThirdOption(getEmployees()); - coolestByPosition.forEach((position, person) -> System.out.println(position + " -> " + person)); + Person qa1 = coolestByPosition1.get("QA"); + Person qa2 = coolestByPosition2.get("QA"); + Person qa3 = coolestByPosition3.get("QA"); + + assertEquals(qa1, qa2); + assertEquals(qa2, qa3); + assertEquals(qa1, qa3); + + Person dev1 = coolestByPosition1.get("dev"); + Person dev2 = coolestByPosition2.get("dev"); + Person dev3 = coolestByPosition3.get("dev"); + + assertEquals(dev1, dev2); + assertEquals(dev2, dev3); + assertEquals(dev1, dev3); + + Person ba1 = coolestByPosition1.get("BA"); + Person ba2 = coolestByPosition2.get("BA"); + Person ba3 = coolestByPosition3.get("BA"); + + assertEquals(ba1, ba2); + assertEquals(ba2, ba3); + assertEquals(ba1, ba3); + +// coolestByPosition3.forEach((position, person) -> System.out.println(position + " -> " + person)); } private static class PersonPositionDuration { @@ -52,31 +76,166 @@ public int getDuration() { } // With the longest duration on single job - private Map getCoolestByPosition(List employees) { + private Map getCoolestByPositionFirstOption(List employees) { + final Stream personPositionDurationStream = + getPersonPositionDurationStream(employees); + // First option // Collectors.maxBy // Collectors.collectingAndThen // Collectors.groupingBy + return personPositionDurationStream + .collect( + groupingBy( + PersonPositionDuration::getPosition, + collectingAndThen( + maxBy(Comparator.comparingInt(PersonPositionDuration::getDuration)), + personPositionDuration -> personPositionDuration.get().getPerson() + ) + ) + ); + } + + private Map getCoolestByPositionSecondOption(List employees) { + final Stream personPositionDurationStream = + getPersonPositionDurationStream(employees); // Second option // Collectors.toMap // iterate twice: stream...collect(...).stream()... - // TODO - throw new UnsupportedOperationException(); + + final Map intermediate = + personPositionDurationStream + .collect( + toMap( + PersonPositionDuration::getPosition, + Function.identity(), + (o1, o2) -> o1.getDuration() > o2.getDuration() ? o1 : o2 + ) + ); + + return intermediate.entrySet() + .stream() + .collect( + toMap( + Map.Entry::getKey, + t -> t.getValue().getPerson()) + ); + } + + private Map getCoolestByPositionThirdOption(List employees) { + final Stream personPositionDurationStream = + getPersonPositionDurationStream(employees); + + // Third option + // new Collector ... + return personPositionDurationStream + .collect(new Collector, + Map>() { + + @Override + public Supplier> supplier() { + return HashMap::new; + } + + @Override + public BiConsumer, PersonPositionDuration> accumulator() { + return (map, ppd) -> { + map.putIfAbsent(ppd.getPosition(), ppd); + PersonPositionDuration current = map.get(ppd.getPosition()); + if (ppd.getDuration() > current.getDuration()) { + map.put(ppd.getPosition(), ppd); + } + }; + } + + @Override + public BinaryOperator> combiner() { + return (accumFirst, accumSecond) -> { + accumSecond.forEach( + (position, ppd) -> + accumFirst.merge( + position, ppd, + (ppd1, ppd2) -> + ppd1.getDuration() > ppd2.getDuration() ? ppd1 : ppd2 + ) + ); + return accumFirst; + }; + } + + @Override + public Function, Map> finisher() { + return map -> + map.entrySet().stream() + .collect( + toMap( + o -> o.getKey(), + t -> t.getValue().getPerson() + ) + ); + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + } + ); } + @Test public void getTheCoolestOne2() { final Map coolestByPosition = getCoolestByPosition2(getEmployees()); - coolestByPosition.forEach((position, person) -> System.out.println(position + " -> " + person)); + final Person qa = coolestByPosition.get("QA"); + assertEquals(new Person("Bob", "Doe", 27), qa); + + final Person dev = coolestByPosition.get("dev"); + assertEquals(new Person("John", "Galt", 26), dev); + + final Person ba = coolestByPosition.get("BA"); + assertEquals(new Person("John", "White", 28), ba); + +// coolestByPosition.forEach((position, person) -> System.out.println(position + " -> " + person)); } // With the longest sum duration on this position // { John Doe, [{dev, google, 4}, {dev, epam, 4}] } предпочтительнее, чем { A B, [{dev, google, 6}, {QA, epam, 100}]} private Map getCoolestByPosition2(List employees) { - // TODO - throw new UnsupportedOperationException(); + return getPersonPositionDurationStream(employees) + .collect( + groupingBy(PersonPositionDuration::getPosition, + collectingAndThen( + groupingBy( + PersonPositionDuration::getPerson, + summingInt(PersonPositionDuration::getDuration) + ), + m -> m.entrySet().stream() + .sorted((o1, o2) -> Integer.compare(o2.getValue(), o1.getValue())) + .map(Map.Entry::getKey) + .findFirst() + .get() + ) + ) + ); + } + + + private Stream getPersonPositionDurationStream(List employees) { + return employees.stream() + .flatMap( + e -> e.getJobHistory().stream() + .map(j -> + new PersonPositionDuration( + e.getPerson(), + j.getPosition(), + j.getDuration() + ) + ) + ); } private List getEmployees() { @@ -115,19 +274,19 @@ private List getEmployees() { new Employee( new Person("John", "White", 25), Collections.singletonList( - new JobHistoryEntry(6, "QA", "epam") + new JobHistoryEntry(7, "QA", "epam") )), new Employee( new Person("John", "Galt", 26), Arrays.asList( - new JobHistoryEntry(3, "dev", "epam"), - new JobHistoryEntry(1, "dev", "google") + new JobHistoryEntry(4, "dev", "epam"), + new JobHistoryEntry(5, "dev", "google") )), new Employee( new Person("Bob", "Doe", 27), Arrays.asList( new JobHistoryEntry(4, "QA", "yandex"), - new JobHistoryEntry(2, "QA", "epam"), + new JobHistoryEntry(4, "QA", "epam"), new JobHistoryEntry(2, "dev", "abc") )), new Employee( diff --git a/src/test/java/part2/exercise/CollectorsExercise2.java b/src/test/java/part2/exercise/CollectorsExercise2.java index 00fda00..63c433c 100755 --- a/src/test/java/part2/exercise/CollectorsExercise2.java +++ b/src/test/java/part2/exercise/CollectorsExercise2.java @@ -1,8 +1,5 @@ package part2.exercise; -import data.Employee; -import data.JobHistoryEntry; -import data.Person; import org.junit.Test; import java.util.*; @@ -16,7 +13,8 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.*; +import static org.junit.Assert.assertEquals; public class CollectorsExercise2 { @@ -109,15 +107,24 @@ public static List generatePairs(int idCount, int length) { private static class SubResult { private final Map> subResult; - private final Map> knownKeys; + private final Map knownKeys; private final Map> valuesWithoutKeys; - public SubResult(Map> subResult, Map> knownKeys, Map> valuesWithoutKeys) { + public SubResult(Map> subResult, Map knownKeys, Map> valuesWithoutKeys) { this.subResult = subResult; this.knownKeys = knownKeys; this.valuesWithoutKeys = valuesWithoutKeys; } + public SubResult(Map knownKeys, Map> valuesWithoutKeys) { + this(new HashMap<>(), knownKeys, valuesWithoutKeys); + } + + public SubResult() { + this(new HashMap<>(), new HashMap<>(), new HashMap<>()); + } + public Map> getSubResult() { return subResult; } @@ -126,7 +133,7 @@ public Map> getValuesWithoutKeys() { return valuesWithoutKeys; } - public Map> getKnownKeys() { + public Map getKnownKeys() { return knownKeys; } } @@ -174,26 +181,69 @@ public void collectKeyValueMap() { // В каждом Map.Entry id ключа должно совпадать с keyId для каждого значения в списке // final Map> keyValuesMap1 = valueMap1.entrySet().stream()... + final Map keyMap1 = + pairs.stream() + .collect( + toMap( + o -> o.getKey().getId(), + t -> t.getKey(), + (u, u2) -> u + ) + ); + + final Map> valuesMap1 = + pairs.stream() + .collect( + groupingBy( + o -> o.getValue().getKeyId(), + mapping(o -> o.getValue(), toList()) + ) + ); + + Map> firstResult = valuesMap1.entrySet() + .stream() + .collect( + toMap( + o -> keyMap1.get(o.getKey()), + o -> o.getValue() + ) + ); // В 1 проход в 2 Map с использованием MapPair и mapMerger final MapPair res2 = pairs.stream() .collect(new Collector() { @Override public Supplier supplier() { - // TODO - throw new UnsupportedOperationException(); + return MapPair::new; } @Override public BiConsumer accumulator() { - // TODO add key and value to maps - throw new UnsupportedOperationException(); + return (mapPair, pair) -> { + mapPair.getKeyById().putIfAbsent(pair.getKey().getId(), pair.getKey()); + mapPair.getValueById().computeIfAbsent(pair.getValue().getKeyId(), + s -> new ArrayList<>()).add(pair.getValue()); + }; } @Override public BinaryOperator combiner() { - // TODO use mapMerger - throw new UnsupportedOperationException(); + return (mapPair1, mapPair2) -> { + BinaryOperator> keyBinaryOperator = mapMerger((o, o2) -> o); + Map mapKeys = + keyBinaryOperator.apply(mapPair1.getKeyById(), mapPair2.getKeyById()); + + BinaryOperator>> valueBinaryOperator = + mapMerger( + (list1, list2) -> { + list1.addAll(list2); + return list1; + }); + Map> mapValues = + valueBinaryOperator.apply(mapPair1.getValueById(), mapPair2.getValueById()); + + return new MapPair(mapKeys, mapValues); + }; } @Override @@ -205,41 +255,78 @@ public Function finisher() { public Set characteristics() { return Collections.unmodifiableSet(EnumSet.of( Characteristics.UNORDERED, - Characteristics.IDENTITY_FINISH)); + Collector.Characteristics.IDENTITY_FINISH)); } }); final Map keyMap2 = res2.getKeyById(); final Map> valuesMap2 = res2.getValueById(); - // final Map> keyValuesMap2 = valueMap2.entrySet().stream()... +// final Map> keyValuesMap2 = valueMap2.entrySet().stream()... + final Map> secondResult = + valuesMap2.entrySet().stream() + .collect( + toMap( + o -> keyMap2.get(o.getKey()), + o -> o.getValue() + ) + ); // Получение результата сразу: - final SubResult res3 = pairs.stream() .collect(new Collector() { @Override public Supplier supplier() { - // TODO - throw new UnsupportedOperationException(); + return SubResult::new; } @Override public BiConsumer accumulator() { - // TODO add key to map, then check value.keyId and add it to one of maps - throw new UnsupportedOperationException(); + return (subResult, pair) -> { + subResult.getKnownKeys().putIfAbsent(pair.getKey().getId(), pair.getKey()); + subResult.getValuesWithoutKeys().computeIfAbsent(pair.getValue().getKeyId(), + s -> new ArrayList<>()).add(pair.getValue()); + + }; } @Override public BinaryOperator combiner() { - // TODO use mapMerger, then check all valuesWithoutKeys - throw new UnsupportedOperationException(); + return (subResult1, subResult2) -> { + BinaryOperator> keyBinaryOperator = mapMerger((o, o2) -> o); + Map mapKeys = + keyBinaryOperator.apply(subResult1.getKnownKeys(), subResult2.getKnownKeys()); + + BinaryOperator>> valueBinaryOperator = + mapMerger( + (list1, list2) -> { + list1.addAll(list2); + return list1; + }); + Map> mapValues = + valueBinaryOperator.apply( + subResult1.getValuesWithoutKeys(), + subResult2.getValuesWithoutKeys() + ); + + return new SubResult(mapKeys, mapValues); + }; } @Override public Function finisher() { - // TODO use mapMerger, then check all valuesWithoutKeys - throw new UnsupportedOperationException(); + return subResult -> { + Map> collect = subResult.getValuesWithoutKeys().entrySet() + .stream() + .collect( + toMap( + t -> subResult.getKnownKeys().get(t.getKey()), + o -> o.getValue() + ) + ); + + return new SubResult(collect, subResult.knownKeys, subResult.getValuesWithoutKeys()); + }; } @Override @@ -249,9 +336,26 @@ public Set characteristics() { } }); - final Map> keyValuesMap3 = res3.getSubResult(); + final Map> thirdResult = res3.getSubResult(); // compare results + firstResult.forEach((key, values) -> { + List secondValues = secondResult.get(key); + assertEquals(values.size(), secondValues.size()); + assertEquals(key.getId(), values.get(0).getKeyId()); + }); + + secondResult.forEach((key, values) -> { + List thirdValues = thirdResult.get(key); + assertEquals(values.size(), thirdValues.size()); + assertEquals(key.getId(), values.get(0).getKeyId()); + }); + + thirdResult.forEach((key, values) -> { + List firstValues = firstResult.get(key); + assertEquals(values.size(), firstValues.size()); + assertEquals(key.getId(), values.get(0).getKeyId()); + }); } }