#java #spring-boot #spring-mvc
#java #весенняя загрузка #spring-mvc
Вопрос:
У меня есть код :
@GetMapping("/goal/{id}")
public String goalInfo(@PathVariable(value = "id") long id, Model model) {
if (!goalRepository.existsById(id)) {
return "redirect:/goal";
}
Iterable<SubGoal> subGoal = subGoalRepository.findAll();
ArrayList<SubGoal> subGoals = new ArrayList<>();
//How refactor this?
for(SubGoal sub : subGoal){
if(sub.getParentGoal().getId().equals(id)){
subGoals.add(sub);
}
}
if(subGoals.size() > 0) {
goalPercent(id, subGoal);
}
Optional<Goal> goal = goalRepository.findById(id);
ArrayList<Goal> result = new ArrayList<>();
goal.ifPresent(result::add);
model.addAttribute("goal", result);
model.addAttribute("subGoal",subGoals);
return "goal/goal-info";
}
Здесь я получаю подцели из репозитория и фильтрую эти значения.
Как я могу сделать это без foreach? Я хочу использовать потоки или что-то еще.
Комментарии:
1. попробуйте subGoal.stream().filter(цель-> цель.getParentGoal().getId().equals(id)).collect(Collectors.toCollection(ArrayList::new))
2. Правильный ответ — не делайте этого в первую очередь . Каждый раз, когда вы фильтруете данные, вы должны делать это как можно ближе к источнику, чтобы уменьшить объем ненужной обработки.
Ответ №1:
Вам не нужно объявлять итерацию в вашем коде, чтобы отфильтровать ваш ArrayList. Метод фильтрации уже предоставляет его для вас. Вы можете использовать:
subGoals = subGoals.stream().filter(subGoal ->
/*Here goes your filter condition*/ ).collect(Collectors.toList());
Комментарии:
1. это даст список. Однако вы можете сделать что-то вроде .collect(Collectors.toCollection(ArrayList::new)), чтобы вернуть arrayLst
2. Спасибо, я попробую
3. Как сказал @Shubham, вместо того, чтобы возвращать ArrayList ,
.collect(Collectors.toList())
возвращает абстрактный список, но он может быть назначен существующему ArrayList без проблем, учитывая концепцию полиморфизма, согласно которой один унаследованный класс может принимать значение суперкласса, но не наоборот.
Ответ №2:
Для преобразования Iterable
в Stream
использование StreamSupport.stream(iter.spliterator(), par)
.
Iterable<SubGoal> subGoal = subGoalRepository.findAll();
List<SubGoal> subGoals = StreamSupport
.stream(subGoal.spliterator(), false)
.filter(sub -> sub.getParentGoal().getId().equals(id))
.collect(toList()) // static import `Collectors.toList()`
...
Кроме того, эта часть может быть также одним оператором.
перед (три оператора)
Optional<Goal> goal = goalRepository.findById(id);
ArrayList<Goal> result = new ArrayList<>();
goal.ifPresent(result::add);
после (одного оператора)
List<Goal> result = goalRepository.findById(id)
.map(goal -> singletonList(goal)) // Collections.singletonList()
.orElse(emptyList()); // Collections.emptyList()
Обновления
1. singletonList()
, emptyList()
Это всего лишь заводские методы, используемые при создании единого списка объектов и пустого списка.
вы можете изменить эту часть любой функции, которая имеет Goal
в качестве входных и List
выходных данных и любой пустой список.
Например,
.map(goal -> Arrays.asList(goal))
.orElse(new ArrayList<>());
or
.map(goal -> {
ArrayList<Goal> l = new ArrayList<>();
l.add(goal);
return l;
})
...
2. Я изменил тип списка на List<Goal>
, а не ArrayList<Goal>
Извините, я пропустил объяснение по этому поводу.
В ООП использование интерфейса будет более эффективной практикой, чем использование конкретного класса во многих ситуациях.
Если вам нужно ArrayList<>
явно использовать Type или по какой-то причине вы хотите указать фактический экземпляр списка, вы также можете использовать toCollection()
, как показано ниже.
.collect(toCollection(ArrayList::new)) // you can specify the actual list instance
Спасибо @John Bollinger @hfontanez за указание на это.
Комментарии:
1. Я думаю, вы имеете в виду
Collectors.toList()
, что нетCollections.toList()
. Однако это собирает в aList
, не обязательно anArrayList
, и OP специально запросил последнее.2. Первый пример по-прежнему неверен в том смысле, что он пытается присвоить a
List<SubGoal>
переменной типаArrayList<SubGoal>
.3. Кроме того,
Collections.emptyList()
выдает неизменяемыйList
, который (следовательно) определенно не являетсяArrayList
. Хотя это может служить целям OP, это также может привести к их серьезному сбою.4. Это работа! Спасибо! Можете ли вы объяснить мне, как работает последний фрагмент кода? Для чего мы используем синглтон?
5. @hfontanez, я, конечно, согласен с тем, что лучше кодировать для интерфейсов, и что настаивать на конкретных конкрециях часто является ошибкой. Это было бы достойной темой для рассмотрения этого ответа. Но этот ответ создает объекты другого типа, чем запрошенный, без комментариев или обсуждения связанных с этим проблем, и это ситуация, которая требует улучшения.
Ответ №3:
Это фильтрация на стороне клиента и крайне неэффективна. Вместо этого просто объявите этот метод в своем интерфейсе репозитория:
Collection<SubGoal> findByParentId(Long id); // or Stream, Iterable