Google Guava and Its Two Fantastic Libraries: Graph and Eventbus
The main idea behind it was to include generics introduced in JDK 1.5 into Java Collections Framework, or JCF, and enhance its capability.
Join the DZone community and get the full member experience.
Join For FreeGuava is an open-source library that developed with Google to meet the daily needs of programmers; actually, Guava prevents code duplication that may be written by programmers all around the world. In addition to eliminating boilerplate codes, Guava makes programming easier and more efficient with better performance. Personally, I think Google used Artificial Intelligence to recognize Java developer's requirements by using their daily searches. Google Guava is a Google project, mainly developed by Google's developers, but it's been open-sourced now. The main idea behind it was to include generics introduced in JDK 1.5 into Java Collections Framework, or JCF, and enhance its capability. In general, Guava consists of three major components:
First, basic utilities to reduce manual labor to implement common methods and behaviors and sometimes boilerplate codes.
Second, a complementary component for current collection frameworks such as Java collections framework (JCF) formerly called the Google Collections Library and apache commons-collections, actually Guava is an extension for JCF but it has many more features than apache common collections you can see a comparison between Google Guava and Apache Commons Collections here.
Third, other utilities which provide convenient and productive features such as functional programming, you can a list of some important Guava utilities in the below table.
category | name | description |
Basic utilities | Using and avoiding null | null can be ambiguous, can cause confusing errors, and is sometimes just plain unpleasant, many Guava utilities reject and fail fast on nulls, rather than accepting them blindly. |
Basic utilities |
Preconditions | Test preconditions for your methods more easily |
Basic utilities |
Common object methods | Simplify implementing Object methods, like hashCode() and toString() . |
Basic utilities |
Ordering | Guava's powerful "fluent Comparator " class. |
Collections | Immutable collections | for defensive programming, constant collections, and improved efficiency |
Collections | New collection types | for use cases that the JDK collections don't address as well as they could: multisets, multimaps, tables, bidirectional maps, and more |
Collections | Powerful collection utilities | for common operations not provided in java.util.Collections |
Collections | Extension utilities | writing a Collection decorator? Implementing Iterator ? We can make that easier |
Graphs | Graph | a graph whose edges are anonymous entities with no identity or information of their own |
Graphs | ValueGraph | a graph whose edges have associated non-unique values |
Graphs | Network | a graph whose edges are unique objects |
Caches | Caches | Local caching, done right, and supporting a wide variety of expiration behaviors |
I/O | I/O | Simplified I/O operations, especially on whole I/O streams and files, for Java 5 and 6 |
Event Bus | EventBus | Publish-subscribe-style communication between components without requiring the components to explicitly register with one another. |
reflection | Reflection | Guava utilities for Java's reflective capabilities |
How to Add Guava to a Project
Thank god, Maven has made life easy; just add the following dependency to your project :
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
Graph
A graph is a data structure consisting of nodes and edges. The nodes are sometimes also referred to as vertices and the edges are lines or arcs that connect any two nodes in the graph. Graphs are a relatively complex data structure that every programmer has encountered at least once and every programmer has to provide their own codes to manipulate and traverse it. Guava graphs come with some utility classes to make programmers convenient to work with graphs.
'Graph<Object>' is the base interface in the package:
com.google.common.graph.Graph
MutableGraph and ImmutableGraph are the two major implementations of it.
Create
You can build a graph with any type with singleton class named GraphBuilder
:
xxxxxxxxxx
//graph of Integers
MutableGraph<Integer> myGraph= GraphBuilder.directed().build();
//graph of Strings
MutableGraph<String> myGraph= GraphBuilder.directed().build();
//graph of any classes
MutableGraph<MyCustomClassInstance> myGraph= GraphBuilder.directed().build();
Add Nodes, Edge
Now, you have a graph named 'myGraph.' Time to add nodes and edges to it.
First, you can add nodes and then add an edge between them:
x
//add nodes 'ul','um'
myGraph.addNode("ul");
myGraph.addNode("um");
//add edge between 'ul','um'
myGraph.putEdge("ul", "um");
or directly add edges :
x
//adds nodes 'ul','um' and then edge between them
myGraph.putEdge("ul", "um");
Remove Nodes, Edges
It's easy! Exactly do the opposite of the previous code:
xxxxxxxxxx
myGraph.removeNode("ul");
myGraph.removeNode("um");
myGraph.removeEdge("ul", "um");
Traverse Graph
com.google.common.graph.Traverser<N>
is a magical class that can traverse the nodes that are reachable from a specified (set of) start node(s).
Traverser has following methods:
breadthFirst(Iterable<? extends N> startNodes) | Iterable over the nodes reachable from any of the startNodes, in the order of a breadth-first traversal |
breadthFirst(N startNode) | Iterable over the nodes reachable from startNode, in the order of a breadth-first traversal |
depthFirstPostOrder(Iterable<? extends N> startNodes) | Iterable over the nodes reachable from any of the startNodes, in the order of a depth-first post-order traversal |
depthFirstPostOrder(N startNode) | Iterable over the nodes reachable from startNode, in the order of a depth-first post-order traversal |
depthFirstPreOrder(Iterable<? extends N> startNodes) | Iterable over the nodes reachable from any of the startNodes, in the order of a depth-first pre-order traversal |
forGraph(SuccessorsFunction<N> graph) | Creates a new traverser for the given general graph. |
forTree(SuccessorsFunction<N> tree) | Creates a new traverser for a directed acyclic graph that has at most one path from the start node(s) to any node reachable from the start node(s), and has no paths from any start node to any other start node, such as a tree or forest |
Let's create and traverse a simple graph using Guava:
x
import com.google.common.graph.*;
public class GraphTest {
static class Node {
private final String name;
public Node(String name) {this.name = name; }
public String toString() {return name; }
}
public static void main(String argv[]) {
Node root = new Node("root");
MutableGraph<Node> myGraph = GraphBuilder.directed().build();
Node node1=new Node(" 1 ");
Node node2=new Node(" 2 ");
Node node3=new Node(" 3 ");
Node node4=new Node(" 4 ");
Node node5=new Node(" 5 ");
myGraph.putEdge(root, node1);
myGraph.putEdge(root, node2);
myGraph.putEdge(root, node3);
myGraph.putEdge(node2, node4);
myGraph.putEdge(node2, node5);
//Print the nodes Depth First
System.out.println("==============Dept First==============");
Traverser.forGraph(myGraph).depthFirstPostOrder(root)
.forEach(x->System.out.println(x));
//Print the nodes Bread First
System.out.println("==============Breath First==============");
Traverser.forGraph(myGraph).breadthFirst(root)
.forEach(x->System.out.println(x));
}
}
-
x
Executaion results:
==============Dept First==============
1
3
5
4
2
root
==============Breath First==============
root
1
3
2
5
4
Process finished with exit code 0
I tried to explain the concepts in a very simple and practical way, and also tried as much as possible not to get too involved in theoretical and academic topics .but if you need more information and more theoretical concepts you can find them here.
Eventbus
An Eventbus is a mechanism that allows different components to communicate with each other without knowing about each other. A component can send an Event to the Eventbus without knowing who will pick it up or how many others will pick it up. Components can also listen to Events on an Eventbus, without knowing who sent the Events. That way, components can communicate without depending on each other. Also, it is very easy to substitute a component. As long as the new component understands the Events that are being sent and received, the other components will never know.
http://www.rribbit.org/eventbus.html
for using Eventbus, first of all, for registering listeners and posting events you need a Bus:
EventBus eventBus=new EventBus();
or every publish-subscribe system, Listeners are a must. Listeners can be created easily with the following codes:
x
public class MyEventListener{
private static int numberEvents;
private static List<String> eventsList=new ArrayList<>();
public void myEvent(String event) {
System.out.println(event);
numberEvents++;
}
As you can see, the handler method has annotated with @Subscribe
; now Listener should be added to the Eventbus and posting event. Here is a simple example:
xxxxxxxxxx
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EventBusTest {
class EventListener{
private int numberEvents;
public void myEvent(String event) {
System.out.println(event);
numberEvents++;
}
public int getNumberEvents(){
return numberEvents;
}
}
public void testEventBusSimpleTest(){
EventBus eventBus=new EventBus();
EventListener listener = new EventListener();
eventBus.register(listener);
eventBus.post("event1");
eventBus.post("event2");
assertEquals(2, listener.getNumberEvents());
}
}
OK! everything looks great. but this code was very simple. Let's solve a real-world problem: take a look at a sample from hackernoon. In this example, we would demonstrate how to use an Eventbus for simulating the payment process:
in this example we have two listeners, PaymentService and RecipieSender that are responsible for the transaction and sending recipes to Buyer and Seller:
xxxxxxxxxx
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.junit.jupiter.api.Test;
import java.util.concurrent.Executors;
public class PaymentService {
static class EventBusFactory {
//create an asynch bus in new Thread
private static EventBus eventBus = new AsyncEventBus(Executors.newCachedThreadPool());
public static EventBus getEventBus() {
return eventBus;
}
}
class Transaction {
private String transactionName;
private double amount = 0.0;
public Transaction(String transactionName, Double amount) {
this.transactionName = transactionName;
this.amount = amount;
}
public String getTransactionName() {
return transactionName;
}
public double getAmount() {
return amount;
}
public String toString() {
return "[transaction with name " + transactionName + " and amount " + amount + "]";
}
}
class Recipie {
private String message;
public Recipie(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public String toString() {
return message;
}
}
//listener
class paymentService {
private void debitBuyer(Transaction transaction) {
System.out.println("going to debit buyer for following transaction : " + transaction);
}
private void creditSeller(Transaction transaction) {
System.out.println("going to credit seller for following transaction : " + transaction);
}
}
//listener
class RecieptSender {
public void sendRecieptToCustomer(Recipie recipie) {
System.out.println("Reciept sent to Customer :" + recipie.message);
}
public void sendRecieptToSeller(Recipie recipie) {
System.out.println("Reciept sent to Seller " + recipie.message);
}
}
public void testPayment() {
//registering listeners
EventBusFactory.getEventBus().register(new paymentService());
EventBusFactory.getEventBus().register(new RecieptSender());
//doing payment proccess
EventBusFactory.getEventBus().post(new Transaction("Buy a ticket", 100D));
//sending message
EventBusFactory.getEventBus().post(new Recipie(" Successful transaction"));
}
}
x
going to debit buyer for following transaction : [transaction with name Buy a ticket and amount 100.0]
going to credit seller for following transaction : [transaction with name Buy a ticket and amount 100.0]
Reciept sent to Customer : Successful transaction
Reciept sent to Seller Successful transaction
So, What Is Next?!
Guava EventBus has amazing integration with Apache Camel you can read more about it here.
Opinions expressed by DZone contributors are their own.
Comments