简体   繁体   中英

Best way to make many generic objects

Background

For class, I made a linear-chaining hash table. It stores an array of linked lists, and whenever get or put is called, the key is hashed and modulo'd by the array size. The get or put is then called on the resulting linked list (which I also implemented).

I have an ST (symbol table) interface. It is much like Map , but some of Map 's required operations were too confusing for me to implement. Implementing this interface I have implementations of a linked list, red-black tree, linear-probing hash table, and the linear-chaining hash table.

I would like to make a something similar to a linear-chaining hash table that accepts an arbitrary delegate symbol table type. For example, initializing it with the red-black tree type would make a table of red-black trees, and the get and put functions would delegate to those red-black trees.

I recognize that my implementations are almost certainly slower than the library-provided ones, and that I would be better off to use those in real code. I am just trying to experiment and learn.

Question

What is the best way to supply a type to my hash table so that the hash table will consist of a table of that type, and calls will delegate to those symbol tables?

I can't use generics because I can't initialize those, and I need to initialize on construction and on re-sizing.

I thought about providing a blank symbol table of the desired type to start, and then using the copy method on that, but it seems like there should be a better way. Is there?

You could require a Factory also be provided that creates instances of the desired backing structure.

A simple example with a Map that delegates to another Map :

public static interface Supplier<T> {

    T get();
}

public static class DelegatingMap<K, V> implements Map<K, V> {

    private final Map<K, V> backingMap;

    public DelegatingMap(final Supplier<Map<K, V>> backingMapSupplier) {
        backingMap = backingMapSupplier.get();
    }

    @Override
    public int size() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean isEmpty() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    //etc...
}

Due to type erasure, at runtime objects don't automatically keep track of their type parameters, which is why new T() doesn't compile.

You can work around this by requiring callers to provide you with the Class object for the type parameter, which you can then use to create new instances. Of course, using reflection bypasses compile time checking (in particular, the compiler will not check that the symbol table implementation has the required constructor), as well as any static analysis your IDE may provide (a call hierarchy on the constructor wouldn't show any callers).

Alternatively, you could require callers to provide you with a factory object, which can create the symbol table implementations on demand. This is more flexible (for instance, the caller can choose constructor arguments) and statically type safe, but also more verbose ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM