Another answer quite abruptly changed the original problem and returned to a simple cycle. I believe this is different code, and therefore it cannot be used to compare runtimes (this loop can also be written in Java, which will give shorter runtimes).
Now try to keep the "streaming method" of the problem.
Remember in advance:
One thing to note in advance. In Java, the granularity of System.currentTimeMillis() can be around 10 ms (!!), which is in the same order of magnitude as the result! This means that the error rate can be huge in Java for 20 ms! So instead, you need to use System.nanoTime() to measure the execution time of the code! See Measuring Time Difference Using System.currentTimeMillis () for more information .
Also, this is not the right way to measure runtime, as starting files for the first time may work several times slower. See Code order and performance for details.
Genesis
Your original Go sentence runs on my computer for about 1.1 seconds , which roughly matches yours.
Removing an interface{} element type
Go has no generics , trying to simulate this behavior using interface{} not the same and have a serious performance impact if the value you want to work with is a primitive type (like int ) or some simple structures (like Go equivalent equivalent to your Java Container type). See: Laws of reflection # Representation of the interface . To package int (or any other specific type) in the interface, you need to create a pair (type; value) that holds the dynamic type and the value that needs to be wrapped (creating this pair also involves copying the wrapped value, see Analysis in the answer How it can contain a snippet?. In addition, if you want to access the value, you must use the assertion type , which is the runtime , so the compiler cannot help with optimization (and verification will add to the runtime of the code)!
So, do not use interface{} for our elements, but instead use a specific type for our case:
type Container struct { value int }
We will use this in the iterator and stream of the following method: Next() Container , and in the mapper function:
type Mapper func(Container) Container
We can also use embedding , since the method set of Iterator is a subset of Stream .
Without further ado, here is a complete, executable example:
package main import ( "fmt" "time" ) type Container struct { value int } type Iterator interface { HasNext() bool Next() Container } type incIter struct { i int } func (it *incIter) HasNext() bool { return it.i < 10000000 } func (it *incIter) Next() Container { it.i++ return Container{value: it.i} } type Mapper func(Container) Container type Stream interface { Iterator Map(Mapper) Stream } type iterStream struct { Iterator } func NewStreamFromIter(it Iterator) Stream { return iterStream{Iterator: it} } func (is iterStream) Map(f Mapper) Stream { return mapperStream{Stream: is, f: f} } type mapperStream struct { Stream f Mapper } func (ms mapperStream) Next() Container { return ms.f(ms.Stream.Next()) } func (ms mapperStream) Map(f Mapper) Stream { return nil
Runtime: 210 ms . It's nice that we have already accelerated it 5 times , but we are far from the performance of Java Stream .
"Removing" Iterator and Stream Types
Since we cannot use generics, the Iterator and Stream interface types do not have to be interfaces, since we need new types if we wanted to use them to define iterators and other types of streams.
So, the following: we remove Stream and Iterator , and we use their specific types, their implementation is higher. This will not damage readability at all, in fact the solution is shorter:
package main import ( "fmt" "time" ) type Container struct { value int } type incIter struct { i int } func (it *incIter) HasNext() bool { return it.i < 10000000 } func (it *incIter) Next() Container { it.i++ return Container{value: it.i} } type Mapper func(Container) Container type iterStream struct { *incIter } func NewStreamFromIter(it *incIter) iterStream { return iterStream{incIter: it} } func (is iterStream) Map(f Mapper) mapperStream { return mapperStream{iterStream: is, f: f} } type mapperStream struct { iterStream f Mapper } func (ms mapperStream) Next() Container { return ms.f(ms.iterStream.Next()) } func main() { s0 := NewStreamFromIter(&incIter{}) s := s0.Map(func(in Container) Container { return Container{value: in.value * 2} }) fmt.Println("Start") start := time.Now() j := 0 for s.HasNext() { s.Next() j++ } fmt.Println(time.Since(start)) fmt.Println("j:", j) }
Execution time: 50 ms , we again accelerated it 4 times in comparison with our previous solution! Now that the Java solution is in the same order of magnitude, and we haven’t lost anything from the “streaming manner”. The total gain from the offer of scams: 22 times faster.
Given the fact that in Java you used System.currentTimeMillis() to measure execution, it might even be the same as Java performance. Asker confirmed: this is the same!
As for the same performance
Now we are talking about the "same" code that does fairly simple basic tasks in different languages. If they perform basic tasks, then not one language can do better than another.
Also keep in mind that Java is a mature adult (over 21 years old) and has great time for development and optimization; in fact, Java JIT (compilation right at the point in time) does a pretty good job for long processes like yours. Go is much younger, still just a child (he will be 5 years old in 11 days), and probably in the foreseeable future, productivity will probably improve than Java.
Further improvements
This "streaming" method may not be "Go" to approach the problem you are trying to solve. This is just the "mirror" code of your Java solution using Go's more idiomatic constructs.
Instead, you should take advantage of the excellent support for Go concurrency, namely goroutines (see go ), which are much more efficient than Java threads, and other language constructs such as channels (see the answer What do golang channels use for? ) And select .
By correctly sorting / breaking initially your large task into smaller ones, the goroutine working pool can be powerful enough to process a large amount of data. See Is this an idiomatic workflow pool in Go?
You also stated in your comment that "I do not have 10M elements for processing, but more than 10G that will not fit into memory." If so, think about the I / O time and the latency of the external system from which you are extracting data from the process. If this takes a considerable amount of time, this can lead to an increase in processing time in the application, and the execution time of the application may not matter (generally).
Go is not going to compress every nanosecond of runtime, but provides you with a simple, minimalist language and tools that make it easy (by writing simple code) to take control and use your available resources (for example, goroutines and a multi-core processor).
(Try to compare. Go to the language specification and the Java language specification . Personally, I read many times, but could never get to the end of Java.)