The question is somewhat controversial, as it seems (+5/-3 at the time of writing this).
As you mentioned, the imperative solution here is most likely the most simple, appropriate and readable one.
The functional or declarative style does not really "fail". It's rather raising questions about the exact goals, conditions and context, and maybe even philosophical questions about language details (like why there is no standard Pair
class in core Java).
You can apply a functional solution here. One simple, technical question is then whether you really want to fill the existing lists, or whether it's OK to create new lists. In both cases, you can use the Collectors#groupingBy
method.
The grouping criterion is the same in both cases: Namely, any "representation" of the specific combination of isA
and isB
of one item. There are different possible solutions for that. In the examples below, I used an Entry<Boolean, Boolean>
as the key.
(If you had further conditions, like isC
and isD
, then you could in fact also use a List<Boolean>
).
The example shows how you can either add the item to existing lists (as in your question), or create new lists (which is a tad simpler and cleaner).
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class FunctionalIfElse
{
public static void main(String[] args)
{
List<Item> items = new ArrayList<Item>();
items.add(new Item(false, false));
items.add(new Item(false, true));
items.add(new Item(true, false));
items.add(new Item(true, true));
fillExistingLists(items);
createNewLists(items);
}
private static void fillExistingLists(List<Item> items)
{
System.out.println("Filling existing lists:");
List<Item> itemIsBoth = new ArrayList<Item>();
List<Item> aItems = new ArrayList<Item>();
List<Item> bItems = new ArrayList<Item>();
Map<Entry<Boolean, Boolean>, List<Item>> map =
new LinkedHashMap<Entry<Boolean, Boolean>, List<Item>>();
map.put(entryWith(true, true), itemIsBoth);
map.put(entryWith(true, false), aItems);
map.put(entryWith(false, true), bItems);
items.stream().collect(Collectors.groupingBy(
item -> entryWith(item.isA(), item.isB()),
() -> map, Collectors.toList()));
System.out.println("Both");
itemIsBoth.forEach(System.out::println);
System.out.println("A");
aItems.forEach(System.out::println);
System.out.println("B");
bItems.forEach(System.out::println);
}
private static void createNewLists(List<Item> items)
{
System.out.println("Creating new lists:");
Map<Entry<Boolean, Boolean>, List<Item>> map =
items.stream().collect(Collectors.groupingBy(
item -> entryWith(item.isA(), item.isB()),
LinkedHashMap::new, Collectors.toList()));
List<Item> itemIsBoth = map.get(entryWith(true, true));
List<Item> aItems = map.get(entryWith(true, false));
List<Item> bItems = map.get(entryWith(false, true));
System.out.println("Both");
itemIsBoth.forEach(System.out::println);
System.out.println("A");
aItems.forEach(System.out::println);
System.out.println("B");
bItems.forEach(System.out::println);
}
private static <K, V> Entry<K, V> entryWith(K k, V v)
{
return new SimpleEntry<K, V>(k, v);
}
static class Item
{
private boolean a;
private boolean b;
public Item(boolean a, boolean b)
{
this.a = a;
this.b = b;
}
public boolean isA()
{
return a;
}
public boolean isB()
{
return b;
}
@Override
public String toString()
{
return "(" + a + ", " + b + ")";
}
}
}
filter
but notelse
. At some point, you will have to have this branching structure. If I am wrong, I will gladly learnt how to do it :) – Historicif
/else
statements are imperative in Java is that they are designed as statements. If it were an expression it would force you to always specify a defaultelse
case so that it yields a value in any case. Long story, short story: Conditional branching is declarative and there is usually no sensible reason to convert it – DashboardItem::getType
and use it inCollectors::groupingBy
– Maurizio(item.isA()? item.isB()? itemIsBoth: aItems: item.isB()? bItems: new ArrayList<>()).add(item);
– Slipshodif
/then
/else
, guards andcase
control structures, which are all expressions and can be nested. – Dashboardswitch
expressions in the future, which is like thecase
expression of Haskell. It’s already included as experimental feature in the recent JDKs. – Slipshod