Complete Guide to Apache Spark Actions¶
Apache Spark actions are operations that trigger the execution of transformations and return a value to the driver program or write data to external storage. Unlike transformations, actions are eager operations that execute immediately. This guide covers all common actions with detailed explanations and Java examples.
Summary¶
Apache Spark provides various actions for different use cases:
| Category | Actions |
|---|---|
| Aggregation | reduce, count, countByKey |
| Retrieval | collect, first, take, takeSample, takeOrdered |
| Persistence | saveAsTextFile, saveAsSequenceFile, saveAsObjectFile |
| Side Effects | foreach |
| Asynchronous | collectAsync, foreachAsync, countAsync |
Key Differences: Transformations vs Actions¶
- Transformations: Lazy, return new RDDs, not executed immediately
- Actions: Eager, return values or write data, trigger computation immediately
Best Practices¶
- Use collect() carefully: Only use on small datasets that fit in driver memory
- Prefer take() over collect(): For inspecting data during development
- Use foreach() for side effects: Writing to databases, updating accumulators
- Choose the right action: Use count() instead of collect().size() for efficiency
- Consider async actions: For parallel execution of multiple actions
- Be cautious with saveAs* actions: They write to distributed storage, ensure proper paths
Actions trigger the actual execution of all lazy transformations in the computation graph, so choosing the right action is crucial for performance and correctness.
1. Reduce Action¶
Overview¶
The reduce() action aggregates the elements of the dataset using a function that takes two arguments and returns one. The function should be commutative and associative for correct parallel computation.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
public class ReduceExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("ReduceExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
// Sum all elements using reduce
Integer sum = numbers.reduce((a, b) -> a + b);
System.out.println("Sum: " + sum);
// Output: Sum: 15
// Find maximum using reduce
Integer max = numbers.reduce((a, b) -> Math.max(a, b));
System.out.println("Max: " + max);
// Output: Max: 5
jsc.close();
}
}
2. Collect Action¶
Overview¶
The collect() action returns all elements of the dataset as an array to the driver program. Should be used only when the result is small enough to fit in memory.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
import java.util.List;
public class CollectExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("CollectExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
// Apply transformation
JavaRDD<Integer> squared = numbers.map(x -> x * x);
// Collect results to driver
List<Integer> result = squared.collect();
System.out.println("Collected: " + result);
// Output: Collected: [1, 4, 9, 16, 25]
jsc.close();
}
}
3. Count Action¶
Overview¶
The count() action returns the number of elements in the dataset as a long value.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
public class CountExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("CountExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
);
// Count total elements
long totalCount = numbers.count();
System.out.println("Total count: " + totalCount);
// Output: Total count: 10
// Count after filtering
long evenCount = numbers.filter(x -> x % 2 == 0).count();
System.out.println("Even count: " + evenCount);
// Output: Even count: 5
jsc.close();
}
}
4. First Action¶
Overview¶
The first() action returns the first element of the dataset. Similar to take(1) but returns a single element instead of an array.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
public class FirstExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("FirstExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(5, 2, 8, 1, 9)
);
// Get first element
Integer first = numbers.first();
System.out.println("First element: " + first);
// Output: First element: 5
// Get first even number
Integer firstEven = numbers.filter(x -> x % 2 == 0).first();
System.out.println("First even: " + firstEven);
// Output: First even: 2
jsc.close();
}
}
5. Take Action¶
Overview¶
The take(n) action returns an array with the first n elements of the dataset.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
import java.util.List;
public class TakeExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("TakeExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
);
// Take first 5 elements
List<Integer> firstFive = numbers.take(5);
System.out.println("First 5: " + firstFive);
// Output: First 5: [1, 2, 3, 4, 5]
// Take after transformation
List<Integer> firstThreeSquared = numbers.map(x -> x * x).take(3);
System.out.println("First 3 squared: " + firstThreeSquared);
// Output: First 3 squared: [1, 4, 9]
jsc.close();
}
}
6. TakeSample Action¶
Overview¶
The takeSample() action returns an array with a random sample of elements from the dataset. You can specify whether sampling is with or without replacement, the number of elements, and an optional random seed.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
import java.util.List;
public class TakeSampleExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("TakeSampleExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
);
// Take random sample without replacement
List<Integer> sample1 = numbers.takeSample(false, 5, 42);
System.out.println("Sample without replacement: " + sample1);
// Take random sample with replacement
List<Integer> sample2 = numbers.takeSample(true, 5, 42);
System.out.println("Sample with replacement: " + sample2);
jsc.close();
}
}
7. TakeOrdered Action¶
Overview¶
The takeOrdered() action returns the first n elements of the RDD using either their natural order or a custom comparator.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class TakeOrderedExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("TakeOrderedExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(5, 2, 9, 1, 7, 3, 8, 4, 6)
);
// Take 5 smallest elements (natural order)
List<Integer> smallest = numbers.takeOrdered(5);
System.out.println("5 smallest: " + smallest);
// Output: 5 smallest: [1, 2, 3, 4, 5]
// Take 5 largest elements (reverse order)
List<Integer> largest = numbers.takeOrdered(5,
Comparator.reverseOrder());
System.out.println("5 largest: " + largest);
// Output: 5 largest: [9, 8, 7, 6, 5]
jsc.close();
}
}
8. SaveAsTextFile Action¶
Overview¶
The saveAsTextFile() action writes the elements of the dataset as a text file (or set of text files) to a specified directory. Spark calls toString() on each element to convert it to text.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
public class SaveAsTextFileExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("SaveAsTextFileExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
// Transform data
JavaRDD<String> output = numbers.map(x -> "Number: " + (x * x));
// Save to text file
output.saveAsTextFile("/tmp/spark-output");
System.out.println("Data saved to /tmp/spark-output");
// Can also save to HDFS
// output.saveAsTextFile("hdfs://namenode:9000/output");
jsc.close();
}
}
9. SaveAsSequenceFile Action¶
Overview¶
The saveAsSequenceFile() action writes elements as a Hadoop SequenceFile. Available on RDDs of key-value pairs that implement Hadoop's Writable interface. (Java and Scala only)
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import scala.Tuple2;
import java.util.Arrays;
public class SaveAsSequenceFileExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("SaveAsSequenceFileExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaPairRDD<String, Integer> pairs = jsc.parallelizePairs(
Arrays.asList(
new Tuple2<>("apple", 5),
new Tuple2<>("banana", 3),
new Tuple2<>("orange", 7)
)
);
// Convert to Writable types
JavaPairRDD<Text, IntWritable> writablePairs = pairs.mapToPair(
tuple -> new Tuple2<>(
new Text(tuple._1()),
new IntWritable(tuple._2())
)
);
// Save as SequenceFile
writablePairs.saveAsSequenceFile("/tmp/sequence-output");
System.out.println("Saved as SequenceFile");
jsc.close();
}
}
10. SaveAsObjectFile Action¶
Overview¶
The saveAsObjectFile() action writes the elements of the dataset using Java serialization, which can be loaded later using SparkContext.objectFile(). (Java and Scala only)
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import java.util.Arrays;
public class SaveAsObjectFileExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("SaveAsObjectFileExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
JavaRDD<Integer> squared = numbers.map(x -> x * x);
// Save as object file
squared.saveAsObjectFile("/tmp/object-output");
System.out.println("Saved as ObjectFile");
// Load it back
JavaRDD<Integer> loaded = jsc.objectFile("/tmp/object-output");
System.out.println("Loaded: " + loaded.collect());
jsc.close();
}
}
11. CountByKey Action¶
Overview¶
The countByKey() action is available only on RDDs of type (K, V). Returns a hashmap of (K, Int) pairs with the count of each key.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;
import java.util.Arrays;
import java.util.Map;
public class CountByKeyExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("CountByKeyExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaPairRDD<String, Integer> pairs = jsc.parallelizePairs(
Arrays.asList(
new Tuple2<>("apple", 1),
new Tuple2<>("banana", 2),
new Tuple2<>("apple", 3),
new Tuple2<>("banana", 4),
new Tuple2<>("apple", 5)
)
);
// Count occurrences of each key
Map<String, Long> counts = pairs.countByKey();
System.out.println("Count by key:");
for (Map.Entry<String, Long> entry : counts.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Output: apple: 3
// banana: 2
jsc.close();
}
}
12. Foreach Action¶
Overview¶
The foreach() action runs a function on each element of the dataset. Usually done for side effects such as updating an Accumulator or interacting with external storage systems.
Important: Modifying variables other than Accumulators outside of foreach() may result in undefined behavior.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.util.LongAccumulator;
import java.util.Arrays;
public class ForeachExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("ForeachExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
// Using accumulator for side effects
LongAccumulator accumulator = jsc.sc().longAccumulator("My Accumulator");
numbers.foreach(x -> {
accumulator.add(x);
System.out.println("Processing: " + x);
});
System.out.println("Accumulator value: " + accumulator.value());
// Output: Accumulator value: 15
// Example with external storage (writing to database, etc.)
numbers.foreach(x -> {
// Insert into database
// saveToDatabase(x);
System.out.println("Would save to DB: " + x);
});
jsc.close();
}
}
Asynchronous Actions¶
Overview¶
Spark RDD API provides asynchronous versions of some actions that immediately return a FutureAction instead of blocking on completion. This allows managing or waiting for asynchronous execution.
Java Example¶
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.JavaFutureAction;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class AsyncActionExample {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("AsyncActionExample");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> numbers = jsc.parallelize(
Arrays.asList(1, 2, 3, 4, 5)
);
// Start async collect
JavaFutureAction<List<Integer>> futureResult = numbers.collectAsync();
System.out.println("Action started asynchronously...");
// Do other work here...
System.out.println("Doing other work...");
try {
// Wait for result
List<Integer> result = futureResult.get();
System.out.println("Result: " + result);
// Output: Result: [1, 2, 3, 4, 5]
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// Can also use foreachAsync
JavaFutureAction<Void> foreachFuture = numbers.foreachAsync(
x -> System.out.println("Async processing: " + x)
);
// Check if done
System.out.println("Is done? " + foreachFuture.isDone());
jsc.close();
}
}