diff --git a/src/hashtable.cpp b/src/hashtable.cpp
index bea549fc398e20b07652cf6b7d9e3eda0eefbb14..55c47d839ebbc669ad76481f924f198869785e0d 100644
--- a/src/hashtable.cpp
+++ b/src/hashtable.cpp
@@ -6,8 +6,9 @@
 #include "hashtable.h"
 
 
-Hashtable::Hashtable(size_t m, float alpha, float s):
-	slot_count(m), max_loadfactor(alpha), growfactor(s), table(new Slot[m])
+template <typename T>
+Hashtable<T>::Hashtable(size_t m, float alpha, float s):
+	slot_count(m), max_loadfactor(alpha), growfactor(s), table(new Slot<T>[m])
 {
 
 	if (!(0 < alpha && alpha < 1))
@@ -16,30 +17,27 @@ Hashtable::Hashtable(size_t m, float alpha, float s):
 		throw std::invalid_argument("grow factor s must be greater 1");
 }
 
-float Hashtable::load_factor() const {
-	return slot_count == 0 ? 0 : static_cast<float>(element_count)/slot_count;
-}
-
-bool Hashtable::insert(unsigned key) {
+template <typename T>
+bool Hashtable<T>::insert(T key) {
 	// Grow the table if inserting would exceed the load factor.
 	// If the key is already in the table this could grow the table unnecessarily once.
 	if (slot_count == 0 || (element_count+1.0f)/slot_count > max_loadfactor)
 		grow();
 
-	unsigned hk = hash(key);
+	size_t hk = hash(key);
 	for (size_t i=0; i<slot_count; i++) {
-		Slot &slot = table[probe(hk, i)];
+		Slot<T> &slot = table[probe(hk, i)];
 
-		if (slot.label == Slot::used && slot.key == key) {
+		if (slot.label == Slot<T>::used && slot.key == key) {
 			// the key is already in the table
 			return false;
 		}
 
-		if (slot.label == Slot::vacant || slot.label == Slot::deleted) {
+		if (slot.label == Slot<T>::vacant || slot.label == Slot<T>::deleted) {
 			// found an empty slot
 			element_count++;
 			slot.key = key;
-			slot.label = Slot::used;
+			slot.label = Slot<T>::used;
 			return true;
 		}
 	}
@@ -49,23 +47,24 @@ bool Hashtable::insert(unsigned key) {
 	throw std::logic_error("will we ever reach here?");
 }
 
-bool Hashtable::remove(unsigned key) {
+template <typename T>
+bool Hashtable<T>::remove(T key) {
 	if (slot_count == 0)
 		return false; // table is empty
 
-	unsigned hk = hash(key);
+	size_t hk = hash(key);
 	for (size_t i=0; i<slot_count; i++) {
-		Slot &slot = table[probe(hk, i)];
+		Slot<T> &slot = table[probe(hk, i)];
 
-		if (slot.label == Slot::vacant) {
+		if (slot.label == Slot<T>::vacant) {
 			// found a vacant slot: key is not in table
 			break;
 		}
 
-		if (slot.label == Slot::used && slot.key == key) {
+		if (slot.label == Slot<T>::used && slot.key == key) {
 			// found the key: mark slot deleted
 			element_count--;
-			slot.label = Slot::deleted;
+			slot.label = Slot<T>::deleted;
 			return true;
 		}
 	}
@@ -73,20 +72,21 @@ bool Hashtable::remove(unsigned key) {
 	return false;
 }
 
-bool Hashtable::contains(unsigned key) {
+template <typename T>
+bool Hashtable<T>::contains(T key) {
 	if (slot_count == 0)
 		return false; // table is empty
 
-	unsigned hk = hash(key);
+	size_t hk = hash(key);
 	for (size_t i=0; i<slot_count; i++) {
-		Slot &slot = table[probe(hk, i)];
+		Slot<T> &slot = table[probe(hk, i)];
 
-		if (slot.label == Slot::vacant) {
+		if (slot.label == Slot<T>::vacant) {
 			// found a vacant slot, key is not in table
 			break;
 		}
 
-		if (slot.label == Slot::used && slot.key == key) {
+		if (slot.label == Slot<T>::used && slot.key == key) {
 			return true;
 		}
 	}
@@ -94,36 +94,44 @@ bool Hashtable::contains(unsigned key) {
 	return false;
 }
 
-inline unsigned Hashtable::hash(unsigned key) {
+template <typename T>
+float Hashtable<T>::load_factor() const {
+	return slot_count == 0 ? 0 : static_cast<float>(element_count)/slot_count;
+}
+
+template <typename T>
+inline size_t Hashtable<T>::hash(T key) {
 	assert(slot_count != 0);
 	return key % slot_count;
 }
 
-inline size_t Hashtable::probe(unsigned h, size_t i) {
+template <typename T>
+inline size_t Hashtable<T>::probe(size_t h, size_t i) {
 	assert(slot_count != 0);
 	return (h+i) % slot_count;
 }
 
-void Hashtable::grow() {
+template <typename T>
+void Hashtable<T>::grow() {
 	size_t old_capacity = slot_count;
 	slot_count = std::ceil(growfactor * (slot_count==0 ? 1 : slot_count));
-	Slot *const new_table = new Slot[slot_count];
+	Slot<T> *const new_table = new Slot<T>[slot_count];
 
 	// for all slots in the old table ...
 	for (size_t i=0; i<old_capacity; i++) {
-		Slot &os = table[i];
+		Slot<T> &os = table[i];
 
 		// that store a key ...
-		if (os.label == Slot::used) {
+		if (os.label == Slot<T>::used) {
 			unsigned hk = hash(os.key);
 
 			// find a slot in the new table
 			for (size_t j=0; j<slot_count; j++) {
-				Slot &ns = new_table[probe(hk, j)];
+				Slot<T> &ns = new_table[probe(hk, j)];
 
-				if (ns.label == Slot::vacant) {
+				if (ns.label == Slot<T>::vacant) {
 					ns.key = os.key;
-					ns.label = Slot::used;
+					ns.label = Slot<T>::used;
 					break;
 				}
 			}
@@ -133,21 +141,23 @@ void Hashtable::grow() {
 	table.reset(new_table); // smart_ptr frees the old table
 }
 
-void Hashtable::info() const {
+template <typename T>
+void Hashtable<T>::info() const {
 	std::cerr << "size: " << size() << ", capacity: " << capacity()
 		<< ", alpha (current/max): " << load_factor() << "/" << max_loadfactor << std::endl;
-	std::cerr << "array size: " << slot_count*sizeof(Slot)/1024 << "kB, start: "
+	std::cerr << "array size: " << slot_count*sizeof(Slot<T>)/1024 << "kB, start: "
 		<< table.get() << ", end: " << table.get()+slot_count << std::endl;
 }
 
-void Hashtable::print() const {
+template <typename T>
+void Hashtable<T>::print() const {
 	for (size_t i=0; i<slot_count; i+=10) {
 		std::cerr << i << ": ";
 		for (size_t j=i; j<i+10 && j<slot_count; j++) {
-			Slot s = table[j];
+			const Slot<T> &s = table[j];
 			std::string repr;
-			if (s.label == Slot::vacant) repr = "F";
-			else if(s.label == Slot::deleted) repr = "D";
+			if (s.label == Slot<T>::vacant) repr = "F";
+			else if(s.label == Slot<T>::deleted) repr = "D";
 			else repr = std::to_string(s.key);
 			std::cerr << repr << "  ";
 		}
diff --git a/src/hashtable.h b/src/hashtable.h
index 94918fe62fe8ba05e2c0e99a07e0351237516b5c..a61e90191699c9e8de1a28fddbde264d6cc4c24c 100644
--- a/src/hashtable.h
+++ b/src/hashtable.h
@@ -4,22 +4,25 @@
 #include <memory>
 
 
+template<typename T> class Hashtable;
+
 /* a bucket of size 1 */
+template <typename T>
 class Slot {
-	unsigned key = 0;
+	T key;
 	enum: char { vacant, deleted, used } label = vacant;
 
-	friend class Hashtable;
+	friend Hashtable<T>;
 };
 
-
+template <typename T>
 class Hashtable {
 	public:
 		Hashtable(size_t m=8, float alpha=0.75, float s=2.0);
 
-		bool insert(unsigned key);
-		bool remove(unsigned key);
-		bool contains(unsigned key);
+		bool insert(T key);
+		bool remove(T key);
+		bool contains(T key);
 		size_t size() const { return element_count; }
 		size_t capacity() const { return slot_count; }
 		float load_factor() const;
@@ -31,11 +34,18 @@ class Hashtable {
 		size_t slot_count;
 		const float max_loadfactor;
 		const float growfactor;
-		std::unique_ptr<Slot[]> table;
+		std::unique_ptr<Slot<T>[]> table;
 
-		unsigned hash(unsigned key);
-		size_t probe(unsigned key, size_t i);
+		size_t hash(T key);
+		size_t probe(size_t h, size_t i);
 		void grow();
 };
 
+// declare some template instantiations explicitly
+// so they are included in the shared library
+template class Hashtable<unsigned int>;
+template class Hashtable<unsigned long>;
+template class Hashtable<unsigned short>;
+template class Hashtable<unsigned char>;
+
 #endif