My question may be too broad and probably the answer is a simple NO, but I have to ask.
Is there any equivalent implementation of (Java 8) streams* in Java 7?
I am familiar with (Java 8) streams but my project requirement is to use Java 7.
*Not to be confused with inputStream and outputStream.
-
5No. But you can look into Scala's stream API which runs in the JVM so you can mix your code. Maybe it's not exactly what you are looking for but it's worth a try if needed.Alexis C.– Alexis C.2015年05月05日 14:20:10 +00:00Commented May 5, 2015 at 14:20
-
I think this is an interesting question. For sure, you need a Java8 JVM regarding the syntactic changes to the language for lambda expressions. But streams itself is a concept that is not implemented by the compiler; but by classes in libraries. It might be a futile activity, but you might want to simply check the streams sources from Java8; and see if they compile&run in a Java7 environment.GhostCat– GhostCat2015年05月05日 14:23:55 +00:00Commented May 5, 2015 at 14:23
-
I believe there are many of them, but not such powerful as Java 8 streams. One example: github.com/wagnerandrade/coollection#readmeAlex Salauyou– Alex Salauyou2015年05月05日 14:24:02 +00:00Commented May 5, 2015 at 14:24
-
@SashaSalauyou I came across that lib some years ago and seems very nice. But I think it does not fit to my current problem since it has to do with numerical data (time-series).Athafoud– Athafoud2015年05月05日 14:35:36 +00:00Commented May 5, 2015 at 14:35
-
As far as I know, Java 8 Streams make use of the new INVOKEDYNAMIC opcode, which only exists in Java 8 JVMs. I do not know if this is strictly necessary, of if it is "just" a performance optimization, though.C-Otto– C-Otto2017年03月07日 13:54:02 +00:00Commented Mar 7, 2017 at 13:54
3 Answers 3
In the official API, no.
There is no more public updates for Java 7. If you're a customer, you might still get minor updates, but this not (or very very very unlikely) for back-porting the Stream API.
With a bit of digging you may look at StreamSupport. I've never tested it but apparently its goal is to backport the Stream API to Java 6/7 and if you want to combine it with lambda expressions there's also retrolambda.
Functional Java can be interesting. It's not exactly the same intent as the Stream API but if your goal is to filter/map/etc. a list/array it might suits your needs. For example:
final List<Integer> b = list(1, 2, 3).map(add.f(-1));
listShow(intShow).println(b); // [0, 1, 2]
Finally you can look into the Scala's Stream API. As Scala runs also on the JVM, you can mix your code. Maybe it's not exactly what you are looking for but it's worth a try if needed.
5 Comments
Google's Guava library contains some functional idioms for Java versions 5 to 7:
https://github.com/google/guava/wiki/FunctionalExplained
Also, there is this library that you may want to check (I haven't heard of it up until some minutes ago when I performed a Google search :-) )
1 Comment
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;
}