An another choice is here for Java 6+
interfaces:
interface TransformRule<In, Out> {
Out extract(In obj);
}
interface FilterRule<T> {
boolean apply(T obj);
}
And Java8Stream-like container class for Collection/Map:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
class FPMapContainer<KeyType, ValueType> extends FPContainer<Map<KeyType, ValueType>, Map.Entry<KeyType, ValueType>, ValueType> {
FPMapContainer(Map<KeyType, ValueType> container) {
super(container);
}
@Override
public <Out> FPMapContainer<KeyType, Out> map(TransformRule<Map.Entry<KeyType, ValueType>, Out> rule) {
return new FPMapContainer<>(handleContainer(getMapMapRule(rule)));
}
@Override
public FPMapContainer<KeyType, ValueType> filter(FilterRule<Map.Entry<KeyType, ValueType>> rule) {
return new FPMapContainer<>(handleContainer(getMapFilterRule(rule)));
}
@Override
public FPMapContainer<KeyType, ValueType> concat(Map<KeyType, ValueType> another) {
HashMap newOne = new HashMap(container);
newOne.putAll(another);
return new FPMapContainer<>(newOne);
}
@Override
public FPMapContainer<KeyType, ValueType> concat(FPContainer<Map<KeyType, ValueType>, Map.Entry<KeyType, ValueType>, ValueType> another) {
return concat(another.get());
}
protected <Out> TransformRule<Map<KeyType, ValueType>, Map<KeyType, Out>> getMapMapRule(final TransformRule<Map.Entry<KeyType, ValueType>, Out> rule) {
return new TransformRule<Map<KeyType, ValueType>, Map<KeyType, Out>>() {
@Override
public Map<KeyType, Out> extract(Map<KeyType, ValueType> obj) {
Map<KeyType, Out> newOne = new HashMap<>();
for (Map.Entry<KeyType, ValueType> entry : obj.entrySet()) {
newOne.put(entry.getKey(), rule.extract(entry));
}
return newOne;
}
};
}
protected TransformRule<Map<KeyType, ValueType>, Map<KeyType, ValueType>> getMapFilterRule(final FilterRule<Map.Entry<KeyType, ValueType>> rule) {
return new TransformRule<Map<KeyType, ValueType>, Map<KeyType, ValueType>>() {
@Override
public Map<KeyType, ValueType> extract(Map<KeyType, ValueType> obj) {
Map<KeyType, ValueType> newOne = new HashMap<>();
for (Map.Entry<KeyType, ValueType> entry : obj.entrySet()) {
KeyType key = entry.getKey();
ValueType value = entry.getValue();
boolean isValid = rule.apply(entry);
if (isValid) {
newOne.put(key, value);
}
}
return newOne;
}
};
}
}
class FPCollectionContainer<ValueType> extends FPContainer<Collection<ValueType>, ValueType, ValueType> {
FPCollectionContainer(Collection<ValueType> container) {
super(container);
}
@Override
public <Out> FPCollectionContainer<Out> map(TransformRule<ValueType, Out> rule) {
return new FPCollectionContainer<>(handleContainer(getCollectionMapRule(rule)));
}
@Override
public FPCollectionContainer<ValueType> filter(FilterRule<ValueType> rule) {
return new FPCollectionContainer<>(handleContainer(getCollectionFilterRule(rule)));
}
@Override
public FPCollectionContainer<ValueType> concat(Collection<ValueType> another) {
ArrayList<ValueType> newOne = new ArrayList<>(container);
newOne.addAll(another);
return new FPCollectionContainer<>(newOne);
}
@Override
public FPCollectionContainer<ValueType> concat(FPContainer<Collection<ValueType>, ValueType, ValueType> another) {
return concat(another.get());
}
protected <Out> TransformRule<Collection<ValueType>, Collection<Out>> getCollectionMapRule(final TransformRule<ValueType, Out> rule) {
return new TransformRule<Collection<ValueType>, Collection<Out>>() {
@Override
public Collection<Out> extract(Collection<ValueType> obj) {
Collection<Out> newOne = new ArrayList<>();
for (ValueType entry : obj) {
newOne.add(rule.extract(entry));
}
return newOne;
}
};
}
protected TransformRule<Collection<ValueType>, Collection<ValueType>> getCollectionFilterRule(final FilterRule<ValueType> rule) {
return new TransformRule<Collection<ValueType>, Collection<ValueType>>() {
@Override
public Collection<ValueType> extract(Collection<ValueType> obj) {
Collection<ValueType> newOne = new ArrayList<>();
for (ValueType entry : obj) {
if (rule.apply(entry)) {
newOne.add(entry);
}
}
return newOne;
}
};
}
}
abstract class FPContainer<ContainerTypeWithValueType, ContainerIterableItemType, ValueType> {
protected ContainerTypeWithValueType container;
protected FPContainer(ContainerTypeWithValueType container) {
this.container = container;
}
public static <KeyType, ValueType> FPMapContainer<KeyType, ValueType> from(Map<KeyType, ValueType> container) {
return new FPMapContainer<>(container);
}
public static <ValueType> FPCollectionContainer<ValueType> from(Collection<ValueType> container) {
return new FPCollectionContainer<>(container);
}
public abstract <Out> Object map(TransformRule<ContainerIterableItemType, Out> rule);
public abstract FPContainer<ContainerTypeWithValueType, ContainerIterableItemType, ValueType> filter(FilterRule<ContainerIterableItemType> rule);
public abstract FPContainer<ContainerTypeWithValueType, ContainerIterableItemType, ValueType> concat(FPContainer<ContainerTypeWithValueType, ContainerIterableItemType, ValueType> another);
public abstract FPContainer<ContainerTypeWithValueType, ContainerIterableItemType, ValueType> concat(ContainerTypeWithValueType another);
public <Out> Out reduce(TransformRule<ContainerTypeWithValueType, Out> rule) {
return rule.extract(container);
}
public ContainerTypeWithValueType get() {
return container;
}
protected <ContainerTargetType> ContainerTargetType handleContainer(TransformRule<ContainerTypeWithValueType, ContainerTargetType> collectionMapRule) {
if (collectionMapRule != null) {
return collectionMapRule.extract(container);
}
return (ContainerTargetType) container;
}
}
Now you can use it like Java8Stream in this way:
TransformRule<Integer, String> integerStringTransform = new TransformRule<Integer, String>() {
@Override
public String extract(Integer obj) {
return "" + obj;
}
};
TransformRule<Collection<String>, String> collectionStringTransform = new TransformRule<Collection<String>, String>() {
@Override
public String extract(Collection<String> obj) {
String result = "";
for (String item : obj) {
result += item;
}
return result;
}
};
FilterRule<Integer> ltFourFilter = new FilterRule<Integer>() {
@Override
public boolean apply(Integer obj) {
return obj != null && obj < 4;
}
};
// ==============================================
String reduced;
// Collection case:
// `reduced` would be "123"
reduced = FPContainer.from(Arrays.asList(1, 4))
.concat(FPContainer.from(Arrays.asList(2)))
.concat(Arrays.asList(3))
.filter(ltFourFilter)
.map(integerStringTransform).reduce(collectionStringTransform);
// Map case:
reduced = FPContainer.from(stringIntegerHashMap)
.filter(new FilterRule<Map.Entry<String, Integer>>() {
@Override
public boolean apply(Map.Entry<String, Integer> obj) {
return obj.getKey().charAt(0) < 'c' && obj.getValue() < 4;
}
})
.map(new TransformRule<Map.Entry<String,Integer>, String>() {
@Override
public String extract(Map.Entry<String, Integer> obj) {
return ""+obj.getValue();
}
}).reduce(new TransformRule<Map<String, String>, String>() {
@Override
public String extract(Map<String, String> obj) {
String result = "";
Map<String, String> objectStringMap = sortByValue(obj);
for (Map.Entry<String, String> entry : objectStringMap.entrySet()) {
result += entry.getKey().toString() + entry.getValue();
}
return result;
}
});
P.S.
sortByValue(map) is here: (credit: https://stackoverflow.com/a/109389/2293635)
public static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Object>() {
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Iterator<Map.Entry<K, V>> it = list.iterator(); it.hasNext();) {
Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}