Skip to content

Commit 7865ec3

Browse files
committed
1_opt_3_HW0_opt2
1 parent 412a039 commit 7865ec3

File tree

2 files changed

+325
-3
lines changed

2 files changed

+325
-3
lines changed

src/main/java/ru/javawebinar/topjava/model/MealTo.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ public class MealTo {
99

1010
private final int calories;
1111

12-
private final boolean excess;
12+
// private final AtomicBoolean excess; // filteredByAtomic (or any ref type, e.g. boolean[1])
13+
// private final Boolean excess; // filteredByReflection
14+
// private final Supplier<Boolean> excess; // filteredByClosure
15+
private boolean excess;
1316

1417
public MealTo(LocalDateTime dateTime, String description, int calories, boolean excess) {
1518
this.dateTime = dateTime;
@@ -18,6 +21,16 @@ public MealTo(LocalDateTime dateTime, String description, int calories, boolean
1821
this.excess = excess;
1922
}
2023

24+
// for filteredByClosure
25+
// public Boolean getExcess() {
26+
// return excess.get();
27+
// }
28+
29+
// for filteredBySetterRecursion
30+
public void setExcess(boolean excess) {
31+
this.excess = excess;
32+
}
33+
2134
@Override
2235
public String toString() {
2336
return "MealTo{" +

src/main/java/ru/javawebinar/topjava/util/MealsUtil.java

Lines changed: 311 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,20 @@
88
import java.time.LocalTime;
99
import java.time.Month;
1010
import java.util.*;
11+
import java.util.concurrent.*;
12+
import java.util.concurrent.locks.ReentrantLock;
13+
import java.util.function.Consumer;
14+
import java.util.function.Predicate;
15+
import java.util.stream.Collector;
1116
import java.util.stream.Collectors;
17+
import java.util.stream.Stream;
1218

19+
import static java.util.function.Function.identity;
20+
import static java.util.stream.Collectors.toList;
1321
import static ru.javawebinar.topjava.util.TimeUtil.isBetweenHalfOpen;
1422

1523
public class MealsUtil {
16-
public static void main(String[] args) {
24+
public static void main(String[] args) throws InterruptedException {
1725
List<Meal> meals = Arrays.asList(
1826
new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 10, 0), "Завтрак", 500),
1927
new Meal(LocalDateTime.of(2020, Month.JANUARY, 30, 13, 0), "Обед", 1000),
@@ -31,13 +39,34 @@ public static void main(String[] args) {
3139
mealsTo.forEach(System.out::println);
3240

3341
System.out.println(filteredByCycles(meals, startTime, endTime, 2000));
42+
43+
// Optional2 recursion
44+
System.out.println(filteredByRecursion(meals, startTime, endTime, 2000));
45+
System.out.println(filteredBySetterRecursion(meals, startTime, endTime, 2000));
46+
System.out.println(filteredByRecursionWithCycleAndRunnable(meals, startTime, endTime, 2000));
47+
48+
// Optional2 reference type
49+
// System.out.println(filteredByAtomic(meals, startTime, endTime, 2000)); // or boolean[1]
50+
// System.out.println(filteredByReflection(meals, startTime, endTime, 2000));
51+
52+
// Optional2 delayed execution
53+
// System.out.println(filteredByClosure(meals, startTime, endTime, 2000));
54+
System.out.println(filteredByExecutor(meals, startTime, endTime, 2000));
55+
System.out.println(filteredByLock(meals, startTime, endTime, 2000));
56+
System.out.println(filteredByCountDownLatch(meals, startTime, endTime, 2000));
57+
System.out.println(filteredByPredicate(meals, startTime, endTime, 2000));
58+
System.out.println(filteredByConsumerChain(meals, startTime, endTime, 2000));
59+
60+
// Optional2 streams
61+
System.out.println(filteredByFlatMap(meals, startTime, endTime, 2000));
62+
System.out.println(filteredByCollector(meals, startTime, endTime, 2000));
3463
}
3564

3665
public static List<MealTo> filteredByStreams(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
3766
Map<LocalDate, Integer> caloriesSumByDate = meals.stream()
3867
.collect(
3968
Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories))
40-
// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum)
69+
// Collectors.toMap(Meal::getDate, Meal::getCalories, Integer::sum)
4170
);
4271

4372
return meals.stream()
@@ -60,6 +89,286 @@ public static List<MealTo> filteredByCycles(List<Meal> meals, LocalTime startTim
6089
return mealsTo;
6190
}
6291

92+
private static List<MealTo> filteredByRecursion(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
93+
ArrayList<MealTo> result = new ArrayList<>();
94+
filterWithRecursion(new LinkedList<>(meals), startTime, endTime, caloriesPerDay, new HashMap<>(), result);
95+
return result;
96+
}
97+
98+
private static void filterWithRecursion(LinkedList<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay,
99+
Map<LocalDate, Integer> dailyCaloriesMap, List<MealTo> result) {
100+
if (meals.isEmpty()) return;
101+
102+
Meal meal = meals.pop();
103+
dailyCaloriesMap.merge(meal.getDate(), meal.getCalories(), Integer::sum);
104+
filterWithRecursion(meals, startTime, endTime, caloriesPerDay, dailyCaloriesMap, result);
105+
if (isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
106+
result.add(createTo(meal, dailyCaloriesMap.get(meal.getDate()) > caloriesPerDay));
107+
}
108+
}
109+
110+
private static List<MealTo> filteredBySetterRecursion(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
111+
class MealNode {
112+
private final MealNode prev;
113+
private final MealTo mealTo;
114+
115+
public MealNode(MealTo mealTo, MealNode prev) {
116+
this.mealTo = mealTo;
117+
this.prev = prev;
118+
}
119+
120+
public void setExcess() {
121+
mealTo.setExcess(true);
122+
if (prev != null) {
123+
prev.setExcess();
124+
}
125+
}
126+
}
127+
128+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
129+
Map<LocalDate, MealNode> mealNodeByDate = new HashMap<>();
130+
List<MealTo> mealsTo = new ArrayList<>();
131+
meals.forEach(meal -> {
132+
LocalDate localDate = meal.getDate();
133+
boolean excess = caloriesSumByDate.merge(localDate, meal.getCalories(), Integer::sum) > caloriesPerDay;
134+
if (TimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
135+
MealTo mealTo = createTo(meal, excess);
136+
mealsTo.add(mealTo);
137+
if (!excess) {
138+
MealNode prevNode = mealNodeByDate.get(localDate);
139+
mealNodeByDate.put(localDate, new MealNode(mealTo, prevNode));
140+
}
141+
}
142+
if (excess) {
143+
MealNode mealNode = mealNodeByDate.remove(localDate);
144+
if (mealNode != null) {
145+
// recursive set for all interval day meals
146+
mealNode.setExcess();
147+
}
148+
}
149+
});
150+
return mealsTo;
151+
}
152+
153+
public static List<MealTo> filteredByRecursionWithCycleAndRunnable(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
154+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
155+
List<MealTo> mealsTo = new ArrayList<>();
156+
Iterator<Meal> iterator = meals.iterator();
157+
158+
new Runnable() {
159+
@Override
160+
public void run() {
161+
while (iterator.hasNext()) {
162+
Meal meal = iterator.next();
163+
caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum);
164+
if (isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
165+
run();
166+
mealsTo.add(createTo(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay));
167+
}
168+
}
169+
}
170+
}.run();
171+
return mealsTo;
172+
}
173+
174+
/*
175+
private static List<MealTo> filteredByAtomic(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
176+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
177+
Map<LocalDate, AtomicBoolean> exceededMap = new HashMap<>();
178+
179+
List<MealTo> mealsTo = new ArrayList<>();
180+
meals.forEach(meal -> {
181+
AtomicBoolean wrapBoolean = exceededMap.computeIfAbsent(meal.getDate(), date -> new AtomicBoolean());
182+
Integer dailyCalories = caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum);
183+
if (dailyCalories > caloriesPerDay) {
184+
wrapBoolean.set(true);
185+
}
186+
if (isBetween(meal.getTime(), startTime, endTime)) {
187+
mealsTo.add(createTo(meal, wrapBoolean)); // also change createTo and MealTo.excess
188+
}
189+
});
190+
return mealsTo;
191+
}
192+
193+
private static List<MealTo> filteredByReflection(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) throws NoSuchFieldException, IllegalAccessException {
194+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
195+
Map<LocalDate, Boolean> exceededMap = new HashMap<>();
196+
Field field = Boolean.class.getDeclaredField("value");
197+
field.setAccessible(true);
198+
199+
List<MealTo> mealsTo = new ArrayList<>();
200+
for (Meal meal : meals) {
201+
Boolean mutableBoolean = exceededMap.computeIfAbsent(meal.getDate(), date -> new Boolean(false));
202+
Integer dailyCalories = caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum);
203+
if (dailyCalories > caloriesPerDay) {
204+
field.setBoolean(mutableBoolean, true);
205+
}
206+
if (isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
207+
mealsTo.add(createTo(meal, mutableBoolean)); // also change createTo and MealTo.excess
208+
}
209+
}
210+
return mealsTo;
211+
}
212+
213+
private static List<MealTo> filteredByClosure(List<Meal> mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
214+
final Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
215+
List<MealTo> mealsTo = new ArrayList<>();
216+
mealList.forEach(meal -> {
217+
caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum);
218+
if (isBetween(meal.getTime(), startTime, endTime)) {
219+
mealsTo.add(createTo(meal, () -> (caloriesSumByDate.get(meal.getDate()) > caloriesPerDay))); // also change createTo and MealTo.excess
220+
}
221+
}
222+
);
223+
return mealsTo;
224+
}
225+
*/
226+
227+
private static List<MealTo> filteredByExecutor(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) throws InterruptedException {
228+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
229+
List<Callable<Void>> tasks = new ArrayList<>();
230+
final List<MealTo> mealsTo = Collections.synchronizedList(new ArrayList<>());
231+
232+
meals.forEach(meal -> {
233+
caloriesSumByDate.merge(meal.getDate(), meal.getCalories(), Integer::sum);
234+
if (isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
235+
tasks.add(() -> {
236+
mealsTo.add(createTo(meal, caloriesSumByDate.get(meal.getDate()) > caloriesPerDay));
237+
return null;
238+
});
239+
}
240+
});
241+
ExecutorService executorService = Executors.newCachedThreadPool();
242+
// https://stackoverflow.com/a/1250668/548473
243+
executorService.invokeAll(tasks);
244+
executorService.shutdown();
245+
return mealsTo;
246+
}
247+
248+
public static List<MealTo> filteredByLock(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) throws InterruptedException {
249+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
250+
List<MealTo> mealsTo = new ArrayList<>();
251+
ExecutorService executor = Executors.newCachedThreadPool();
252+
ReentrantLock lock = new ReentrantLock();
253+
lock.lock();
254+
for (Meal meal : meals) {
255+
caloriesSumByDate.merge(meal.getDateTime().toLocalDate(), meal.getCalories(), Integer::sum);
256+
if (isBetweenHalfOpen(meal.getDateTime().toLocalTime(), startTime, endTime))
257+
executor.submit(() -> {
258+
lock.lock();
259+
mealsTo.add(createTo(meal, caloriesSumByDate.get(meal.getDateTime().toLocalDate()) > caloriesPerDay));
260+
lock.unlock();
261+
});
262+
}
263+
lock.unlock();
264+
executor.shutdown();
265+
executor.awaitTermination(5, TimeUnit.SECONDS);
266+
return mealsTo;
267+
}
268+
269+
private static List<MealTo> filteredByCountDownLatch(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) throws InterruptedException {
270+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
271+
List<MealTo> mealsTo = Collections.synchronizedList(new ArrayList<>());
272+
CountDownLatch latchCycles = new CountDownLatch(meals.size());
273+
CountDownLatch latchTasks = new CountDownLatch(meals.size());
274+
for (Meal meal : meals) {
275+
caloriesSumByDate.merge(meal.getDateTime().toLocalDate(), meal.getCalories(), Integer::sum);
276+
if (isBetweenHalfOpen(meal.getDateTime().toLocalTime(), startTime, endTime)) {
277+
new Thread(() -> {
278+
try {
279+
latchCycles.await();
280+
} catch (InterruptedException e) {
281+
e.printStackTrace();
282+
}
283+
mealsTo.add(createTo(meal, caloriesSumByDate.get(meal.getDateTime().toLocalDate()) > caloriesPerDay));
284+
latchTasks.countDown();
285+
}).start();
286+
} else {
287+
latchTasks.countDown();
288+
}
289+
latchCycles.countDown();
290+
}
291+
latchTasks.await();
292+
return mealsTo;
293+
}
294+
295+
public static List<MealTo> filteredByPredicate(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
296+
Map<LocalDate, Integer> caloriesSumByDate = new HashMap<>();
297+
List<MealTo> mealsTo = new ArrayList<>();
298+
299+
Predicate<Boolean> predicate = b -> true;
300+
for (Meal meal : meals) {
301+
caloriesSumByDate.merge(meal.getDateTime().toLocalDate(), meal.getCalories(), Integer::sum);
302+
if (TimeUtil.isBetweenHalfOpen(meal.getDateTime().toLocalTime(), startTime, endTime)) {
303+
predicate = predicate.and(b -> mealsTo.add(createTo(meal, caloriesSumByDate.get(meal.getDateTime().toLocalDate()) > caloriesPerDay)));
304+
}
305+
}
306+
predicate.test(true);
307+
return mealsTo;
308+
}
309+
310+
public static List<MealTo> filteredByConsumerChain(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
311+
Map<LocalDate, Integer> caloriesPerDays = new HashMap<>();
312+
List<MealTo> result = new ArrayList<>();
313+
Consumer<Void> consumer = dummy -> {
314+
};
315+
316+
for (Meal meal : meals) {
317+
caloriesPerDays.merge(meal.getDate(), meal.getCalories(), Integer::sum);
318+
if (TimeUtil.isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
319+
consumer = consumer.andThen(dummy -> result.add(createTo(meal, caloriesPerDays.get(meal.getDateTime().toLocalDate()) > caloriesPerDay)));
320+
}
321+
}
322+
consumer.accept(null);
323+
return result;
324+
}
325+
326+
private static List<MealTo> filteredByFlatMap(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
327+
Collection<List<Meal>> list = meals.stream()
328+
.collect(Collectors.groupingBy(Meal::getDate)).values();
329+
330+
return list.stream()
331+
.flatMap(dayMeals -> {
332+
boolean excess = dayMeals.stream().mapToInt(Meal::getCalories).sum() > caloriesPerDay;
333+
return dayMeals.stream().filter(meal ->
334+
isBetweenHalfOpen(meal.getTime(), startTime, endTime))
335+
.map(meal -> createTo(meal, excess));
336+
}).collect(toList());
337+
}
338+
339+
private static List<MealTo> filteredByCollector(List<Meal> meals, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {
340+
final class Aggregate {
341+
private final List<Meal> dailyMeals = new ArrayList<>();
342+
private int dailySumOfCalories;
343+
344+
private void accumulate(Meal meal) {
345+
dailySumOfCalories += meal.getCalories();
346+
if (isBetweenHalfOpen(meal.getTime(), startTime, endTime)) {
347+
dailyMeals.add(meal);
348+
}
349+
}
350+
351+
// never invoked if the upstream is sequential
352+
private Aggregate combine(Aggregate that) {
353+
this.dailySumOfCalories += that.dailySumOfCalories;
354+
this.dailyMeals.addAll(that.dailyMeals);
355+
return this;
356+
}
357+
358+
private Stream<MealTo> finisher() {
359+
final boolean excess = dailySumOfCalories > caloriesPerDay;
360+
return dailyMeals.stream().map(meal -> createTo(meal, excess));
361+
}
362+
}
363+
364+
Collection<Stream<MealTo>> values = meals.stream()
365+
.collect(Collectors.groupingBy(Meal::getDate,
366+
Collector.of(Aggregate::new, Aggregate::accumulate, Aggregate::combine, Aggregate::finisher))
367+
).values();
368+
369+
return values.stream().flatMap(identity()).collect(toList());
370+
}
371+
63372
private static MealTo createTo(Meal meal, boolean excess) {
64373
return new MealTo(meal.getDateTime(), meal.getDescription(), meal.getCalories(), excess);
65374
}

0 commit comments

Comments
 (0)