Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to make sure that two identical objects don't exist in JVM?

Tags:

java

Consider this database model:

Book
isbn primary key
title

In a RDBMS, the database makes sure that two identical rows don't exist for the above model.

Similarly, in Java consider this object model:

Book
- isbn: int
- title: String
+ Book(isbn)

Let's say we are creating a Book object:

Book b = new Book(123456);

Later, in some other part of the code we are creating again an identical Book object:

Book c = new Book(123456);

Can Java make sure that no two objects exist in the JVM heap if they are identical? Just like a RDBMS does?

like image 569
Faisal Avatar asked Nov 24 '25 17:11

Faisal


1 Answers

There's no built-in mechanism in Java that automatically does this for you. You could build something for this, but probably shouldn't. And if you do, then probably not in the way that you show in your question.

First: let's assume that these objects are immutable, so the problem is reduced to "let no two objects be constructed that have the same attributes". This is not a necessary restriction, but this way I can already demonstrate the issues with this approach.

The first issue is that it requires you to keep track of each Book instance in your program in a single central place. You can do that quite easily by having a collection that you fill when an object is constructed.

However, this basically builds a massive memory leak into your program because if nothing else hangs on to this Book object, that collection still will reference it, preventing it from being garbage collected.

You can work around that issue by using WeakReference object to hold on to your Book objects.

Next, if you want to avoid duplicates, you almost certainly want a way to fetch the "original" instance of a Book if you can't create a new one. You can't do that if you simply use the constructor, since the constructor can't "return another object", it will always create and return a new object.

So instead of new Book(12345) you want something like BookFactory.getOrCreateBook(12345). That factory can then either fetch the existing Book object with the given id or create a new one, as required.

One way to make the memory leak issue easier to handle (and also to potentially allow multiple parallel sessions each with their own set of unique Book objects) is to make the BookFactory be a BookSession: i.e. you instantiate one and it keeps tracks of its books. Now that BookSession is the "root" of all Books and if it no longer gets referenced it (and all the books it created) can potentially be garbage collected.

All of this doesn't even get into thread safety which is solvable reasonably easily for immutable objects but can get quite convoluted if you want to allow modifications while still maintaining uniqueness.

A simple BookSession could look a little like this (note that I use a record for book only for brevity of this sample code, this would leave the constructor visible. In "real" code I'd use an equivalent normal class where the constructor isn't accessible to others):

record Book(int isbn, String title) {}

class BookSession {
    private final ConcurrentHashMap<Integer, Book> books = new ConcurrentHashMap<>();

    public Optional<Book> get(int isbn) {
        return Optional.ofNullable(books.get(isbn));
    }

    public Book getOrCreate(int isbn, String title) {
        return books.computeIfAbsent(isbn, (i) -> new Book(i, title));
    }
}

You can easily add other methods to the session (such as findByTitle or something like that).

And if you only ever want a single BookSession you could even have a public static final BookSession BOOKS somewhere, if you wanted (but at that point you have re-created the memory leak)

like image 160
Joachim Sauer Avatar answered Nov 26 '25 08:11

Joachim Sauer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!