I recommend implementing Flyweight Pattern. From wikipedia:
A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
Java already implements this in Integer class through the static factory method Integer#valueOf method and the internal IntegerCache class that holds by default Integers from -128 til 127. Here's an example:
Integer i1, i2, i3;
i1 = 100; //it will call Integer with value of 100 from cache
i2 = Integer.valueOf(100); //it will call Integer with value of 100 from cache
i3 = new Integer(100); //creates a new instance
System.out.println(i1 == i2); //true
System.out.println(i1 == i3); //false
System.out.println(i1.equals(i2)); //true
System.out.println(i1.equals(i3)); //true
Knowing this, you can create your own flyweight implementation:
public class YourClass {
    private final String name;
    private final String description;
    //more fields...
    //making a private constructor in case you don't want other classes
    //to create instance of this class carelessly
    //like Integer
    private YourClass(String name, String description) {
        this.name = name;
        this.description = description;
        //probably more logic here
    }
    public String getName() {
        return name;
    }
    public String getDescription() {
        return description;
    }
    @Override
    public int hashCode() {
        //sample implementation
        //it can be heavily improved
        return name.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        //sample implementation
        //it MUST be heavily improved
        if (o == this) return true;
        if (!(o instanceof YourClass)) return false;
        YourClass other = (YourClass)o;
        return this.name.equals(other.getName());
    }
    //static flyweight manager
    private static class YourClassFlyweight {
        //cache with weak entries
        //wrapped into a synchronized Map
        static final Map<String, YourClass> cache =
            Collections.synchronizedMap(
                new WeakHashMap<String, YourClass>());
        //if you don't want weak entries
        //then just use a ConcurrentHashMap
        //static final Map<String, YourClass> cache =
        //    new ConcurrentHashMap<String, YourClass>()));
        private YourClassFlyweight() { }
    }
    //using Factory Method along with this flyweight implementation
    public static YourClass create(String name, String description) {
        //check if it's not created
        if (YourClassFlyweight.cache.containsKey(name)) {
            //if it is, then return current instance
            return YourClassFlyweight.cache.get(name);
        }
        //otherwise, create the instance and add it into cache
        synchronized(YourClassFlyweight.cache) {
            YourClass newInstance = new YourClass(name, description);
            YourClassFlyweight.cache.put(name, newInstance);
            return newInstance;
        }
    }
}
Basic test for this:
YourClass ins1 = YourClass.create("Luiggi", "Mendoza");
YourClass ins2 = YourClass.create("Luiggi", "OtherLastName");
System.out.println(ins1.equals(ins2)); // true
System.out.println(ins1 == ins2); // unbelievably, true
Also, instead using Map with some implementation for a cache, you may use a real cache library like ehcache instead.