Object Pooling is a design pattern that belongs to creational design patterns. Creational design patterns are the ones that deal with object creation mechanism. This gives the program more flexibility in deciding which objects need to be created for a given use case.
Design pattern object pooling gives us the ability to load some instances before any object, so we are able to borrow any of the pre-loaded instances from the reusable pool class.
Using the keyword new to create multiple instances of the object every time, is costly. This cost is related to the time a new instance needs to be created.
The fact that we are able to borrow some instances of an object and return them back to the pool when we don’t need them is very useful.
Object pooling is usually used in games. For instance, in our game, there are some characters who have a gun and shoot. Every time that we use the keyword new to create a new bullet our game is becoming slower and doesn’t work as we would expect.
So, in this case, it is not only the right decision to use object pooling, but it’s mandatory.
Also, it’s a good practice to use it in our characters and some other points of our game.
Now we are ready to see a simple example of object pooling usage.
Using Object Pooling
Our game is simple, the user has to connect the dots so as to make something known to us, like an elephant, a man, flowers e.t.c.
The game is going to have a lot of levels, so, that means we will create and destroy dots all the time. So, it is a great chance to use object pooling pattern.
Let’s start with the Dot class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
public class Dot { private int posX = 0; private int posY = 0; private int radious = 5; private String UUID; private boolean taken = false; private Shape circle; public Dot(String UUID){ this.UUID = UUID; int x = Main.W_WIDTH; // get window width int y = Main.W_HEIGHT; // get window height Random rand = new Random(); posX = rand.nextInt(x - 10) + 5; // generate random x point of circle posY = rand.nextInt(y - 10) + 5; // generate random y point of circle circle = new Ellipse2D.Float(posX, posY, radious * 2, radious * 2); // create a circle } public boolean isColliding(Line line){ return taken = circle.getBounds2D().intersectsLine((Line2D) line.getShape()); // check if circle intersects with line } public boolean isTaken(){ return taken; } public int getX(){ return posX; } public int getY(){ return posY; } public int getRadious(){ return radious; } public Shape getShape(){ return circle; } public String getUUID(){ return UUID; } } |
As you can see this class creates only a dot. The code below creates the dot.
circle = new Ellipse2D.Float(posX, posY, radious * 2, radious * 2);
Additionally, a function named isColliding() checks if this dot intersects with a line.
The next step is to create an abstract Pool class. You can see the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public abstract class Pool<T> { private int size; protected boolean shutdown = false; protected BlockingQueue objects; public Pool(int size) { this.size = size; init(); } private void init(){ objects = new LinkedBlockingQueue(); for (int i = 0; i < size; i++) { addObj(); } } public int size(){ return objects.size(); } public void shutdown(){ shutdown = true; objects.clear(); } public abstract void addObj(); public abstract void free(T t); public abstract T get(); } |
The constructor calls method init(). This method initializes the object with instances of Dot class.
Method addObj() is an abstract function. Also, there are 2 more methods, method size() returns the size of the object and the second removes all of the elements from objects.
DotPool class is extended by an abstract Pool class. Let’s take a look at the code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
public class DotPool extends Pool { public DotPool(int size) { super(size); } @Override public void addObj() { objects.add(new Dot(UUID.randomUUID().toString())); } @Override public Dot get() { if(!shutdown){ if(objects.size() == 0){ // if object is empty then create a new one addObj(); } Dot dot = null; try { dot = (Dot) objects.take(); // retrieves and removes the head of this queue, waiting if necessary until an element becomes available. } catch (InterruptedException e) { e.printStackTrace(); } return dot; } throw new IllegalStateException("Shutdown."); } @Override public void free(Object o) { boolean isOffered = objects.offer(o); // inserts the specified element at the tail of this queue if it is possible to do. if(!isOffered){ throw new NullPointerException("Offer object is null"); } } public void printAllDots(){ int s = objects.size(); Iterator io = objects.iterator(); while (io.hasNext()){ Dot d = (Dot) io.next(); System.out.println("Pool contains Dot with UUID = " + d.getUUID()); } } } |
Method addObj() adds a new instance in objects.
Method get() returns an instance of Dot if it exists, otherwise, it creates a new one. Also, it is a good practice, if there is not any available instance, to wait to get back one instead of creating a new one.
Method free(Object o) returns an instance to objects.
Last but not least, the Main class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
public class Main extends JPanel { public static int W_WIDTH = 400; public static int W_HEIGHT = 400; private DotPool dPool; private ArrayList<Dot> dots; private Line line; public Main() { dPool = new DotPool(2); dots = new ArrayList<Dot>(); line = new Line(); test(); } private void test(){ System.out.println("Pooling Usage"); System.out.println("Init Pool Size " + dPool.size()); dots.add(dPool.get()); System.out.println("Get One Dot from Pool with UUID = " + dots.get(0).getUUID()); System.out.println("Pool Size " + dPool.size()); dots.add(dPool.get()); System.out.println("Get One Dot from Pool with UUID = " + dots.get(1).getUUID()); System.out.println("Pool Size " + dPool.size()); dPool.free(dots.get(1)); dots.remove(1); // remove the Dot from ArrayList System.out.println("Free object with UUID " + dots.get(0).getUUID()); System.out.println("Pool Size " + dPool.size()); dots.add(dPool.get()); System.out.println("Get One Dot from Pool with UUID = " + dots.get(1).getUUID()); System.out.println("Pool Size " + dPool.size()); dots.add(dPool.get()); System.out.println("Get One Dot from Pool with UUID = " + dots.get(2).getUUID()); System.out.println("Pool Size " + dPool.size()); System.out.println("Free All Objects"); int dotsSize = dots.size(); int i = 0; for (i = 0; i < dotsSize; i++) { dPool.free(dots.get(i)); } System.out.println("Now Pool contains " + dPool.size() + " objects"); dPool.printAllDots(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.BLACK); // set the color to black int poolSize = dPool.size(); for (int i = 0; i < poolSize; i++) { dPool.free(dots.get(i)); Shape shape = dPool.get().getShape(); // get the shape (circle) g2.draw(shape); // draw the circle g2.fill(shape); // fill the circle with color (black) } g2.draw(line.getShape()); // draw the line } public static void main(String[] arg) { JFrame frame = new JFrame("Object Pooling"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(W_WIDTH, W_HEIGHT); frame.getContentPane().add(new Main()); frame.setVisible(true); } } |
OUTPUT:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
Pooling Usage Init Pool Size 2 Get One Dot from Pool with UUID = 909a84dd-afca-4eec-b531-d5a71bbcc699 Pool Size 1 Get One Dot from Pool with UUID = 618cb48e-2f51-433d-9b5a-48f715871c7b Pool Size 0 Free object with UUID 909a84dd-afca-4eec-b531-d5a71bbcc699 Pool Size 1 Get One Dot from Pool with UUID = 618cb48e-2f51-433d-9b5a-48f715871c7b Pool Size 0 Get One Dot from Pool with UUID = 7287e81c-6571-4073-b991-be18593ff04e Pool Size 0 Free All Objects Now Pool contains 3 objects Pool contains Dot with UUID = 909a84dd-afca-4eec-b531-d5a71bbcc699 Pool contains Dot with UUID = 618cb48e-2f51-433d-9b5a-48f715871c7b Pool contains Dot with UUID = 7287e81c-6571-4073-b991-be18593ff04e |
And this is the window that our code produces.

As you can see, Pool starts with 2 instances of Dot class. We get one instance and 1 remains in Pool. We get one more instance and we can see that size of the Pool is 0.
In the next step, we release the second instance back to the Pool, and Pool’s size is 1 again.
After that, we ask 2 more instances from Pool, but only one exists. So, Pool will create a new instance.
Now we release all the instances back to the Pool.
As you can see, Pool starts with 2 instances but in the end, it has 3. This has happened because we asked 3 instances and get() function is able to create a new instance if there is not any in Pool.
You can find the code in my GitHub account.
If there is any mistake or you have any questions, please let me know.
If you want to read more about creational patterns and object pooling you can read these books Design Patterns: Elements of Reusable Object-Oriented Software and Game Programming Patterns.