29

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.

naXa stands with Ukraine
38.5k25 gold badges207 silver badges277 bronze badges
asked May 5, 2015 at 14:19
5
  • 5
    No. 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. Commented 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. Commented 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#readme Commented 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). Commented 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. Commented Mar 7, 2017 at 13:54

3 Answers 3

26

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.

Solomon Ucko
6,1803 gold badges28 silver badges48 bronze badges
answered May 5, 2015 at 14:24

5 Comments

Java 7 can still be used for production environments - the "end of life" is just for the free updates. If a customer buys some support from Oracle he will still get updates and bugfixes (even for previous releases like Java 6 or Java 5). So in that case it is perfecly okay to develop software with target runtime Java 7.
The idea of mixing the java with scalla code is very interesting. streamsupport is also good way to go.
@mschenk74 I didn't say it shouldn't be used for production environments. You might get minor updates, but I wanted to point that these updates are very unlikely to backport the Stream API into Java 7, IMO.
Alright - one could read your sentence as "do not program for java 7 since it has reached EOL" - I just wanted to make clear that it is still possible to get updates. But you are perfectly right when you write that there will be no backports of new features.
@mschenk74 I updated my answer to avoid confusions :-)
9

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 :-) )

http://www.functionaljava.org/

answered May 5, 2015 at 14:23

1 Comment

I think 'functional' is the magic word for the google search, I did not thought it earlier. I was not aware for that functionality of Guava, but the documentation looks good. functionaljava.org looks a little 'messy' but I will give it a try!
3

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;
}
answered Nov 14, 2017 at 13:10

1 Comment

I think the most important thing from Streams is the support for parallelism more than support for functional programming.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.