Legacy Classes in Java: Vector, Stack, Hashtable and Enumeration - Quiz
Total: 7 questions
1. What is a legacy class in Java? Which classes are legacy, and are they marked @Deprecated?
What is a legacy class in Java? Which classes are legacy, and are they marked @Deprecated?
Legacy classes in Java are the classes and the interface of the java.util package from Java 1.0 that predate the Collections Framework (Java 1.2): Vector, Stack, Dictionary, Hashtable, Properties and the Enumeration interface. They had no common base: each one has its own method names (addElement(), elementAt(), keys()), and iteration went through Enumeration.
They are kept for backward compatibility. Java 1.2 retrofitted them into the new hierarchy (Vector implements List, Hashtable implements Map), and Java 5 made most of them generic (except Properties, which extends Hashtable<Object, Object>).
None of them is marked @Deprecated: the compiler gives no warnings, and there are no plans to remove them from the JDK. Only the documentation says they are outdated: the Stack Javadoc recommends Deque, and the Dictionary Javadoc calls the class obsolete. New code uses ArrayList, ArrayDeque, HashMap, ConcurrentHashMap and Iterator instead; Properties is still used for configuration.
2. What is the difference between a legacy class and a utility class in Java? Is Collections a legacy class?
What is the difference between a legacy class and a utility class in Java? Is Collections a legacy class?
The two terms describe different things. “Legacy” is about a class's age: it appeared in Java 1.0, before the Collections Framework, and is kept for compatibility (Vector, Hashtable, Enumeration). “Utility” is about its design: the class contains only static methods and is never instantiated (Collections, Arrays, Objects, Math).
Collections is a utility class, but not a legacy one: it appeared in Java 1.2 together with the new framework and is not considered outdated. Do not mix them up: legacy classes such as Vector are ordinary classes whose objects are created with new.
3. How does Vector differ from ArrayList? Does synchronization make Vector thread-safe?
How does Vector differ from ArrayList? Does synchronization make Vector thread-safe?
Vector is a dynamic array from Java 1.0; since Java 1.2 it extends AbstractList and implements List. Differences from ArrayList:
- every method is synchronized, so single-threaded code pays for locks it does not need;
- old methods are still there:
addElement(),elementAt(),setElementAt(obj, index)andinsertElementAt(obj, index)(element first, then index — the reverse ofset()andadd(index, e)), andelements(), which returns anEnumeration; - the default capacity is 10 and the array doubles when full; the
Vector(initialCapacity, capacityIncrement)constructor sets a fixed growth step, andcapacity()shows the length of the internal array.ArrayListgrows by 1.5 times and has neither that parameter nor that method.
Synchronized does not mean thread-safe: each method is synchronized on its own, but not a combination of them. if (!v.contains(x)) v.add(x); run from two threads can add x twice, and iteration has to be wrapped in synchronized (v) { ... } by hand. Multithreaded code uses classes from java.util.concurrent, for example CopyOnWriteArrayList.
4. What are Dictionary and Hashtable, how are they related to the Map interface, and how does Hashtable differ from HashMap?
What are Dictionary and Hashtable, how are they related to the Map interface, and how does Hashtable differ from HashMap?
Dictionary<K, V> is an abstract class from Java 1.0 for key-value pairs with only abstract methods (put(), get(), remove(), size(), isEmpty(), keys(), elements()). It is a class, not an interface, so a subclass cannot extend anything else — one of the reasons the Map interface appeared in Java 1.2. The Javadoc calls Dictionary obsolete, but there is no @Deprecated annotation.
Hashtable<K, V> extends Dictionary and has implemented Map since Java 1.2. Unlike HashMap, all its methods are synchronized, and neither keys nor values may be null — put() throws NullPointerException (HashMap allows one null key and any number of null values).
Gotcha: the old contains(value) method searches the values, so it equals containsValue(), not containsKey(). Old-style iteration uses keys() and elements(), which return a non-fail-fast Enumeration.
5. Why does props.getProperty("port") return null after props.put("port", 8080)? How should you work with Properties?
Why does props.getProperty("port") return null after props.put("port", 8080)? How should you work with Properties?
Properties is declared as public class Properties extends Hashtable<Object, Object> and did not become generic in Java 5. The inherited put() and putAll() accept any object, while the methods of Properties itself work with strings only: getProperty() returns null for a value that is not a String (here it is an Integer), and store() throws ClassCastException on such a value. So write settings with setProperty(String, String) and read them with getProperty().
getProperty(key, defaultValue)returns the default instead ofnull, but does not store it.- The
new Properties(defaults)constructor attaches a defaults set: missing keys are looked up there, but they are not copied into the object, andsize()does not count them. load(InputStream)reads the file as ISO-8859-1, so non-Latin-1 characters without escaping get garbled; read it through a UTF-8Reader:Files.newBufferedReader(Path.of("app.properties")).
It is the only legacy class still used in new code; since Java 9 its data is stored internally in a ConcurrentHashMap.
6. How does the Enumeration interface differ from Iterator, and how do you convert one into the other?
How does the Enumeration interface differ from Iterator, and how do you convert one into the other?
Enumeration<E> is the Java 1.0 iteration interface with hasMoreElements() and nextElement() (which throws NoSuchElementException when there are no more elements). It was superseded by Iterator (Java 1.2) with hasNext() and next(). Differences:
Enumerationcannot remove elements, whileIteratorhasremove();- the enumerations returned by
Vector.elements()andHashtable.keys()/elements()are not fail-fast: if you remove an element during enumeration, the next element may be silently skipped, whereas an iterator throwsConcurrentModificationException.
Enumeration is still returned by legacy classes, StringTokenizer, ClassLoader.getResources() and NetworkInterface.getNetworkInterfaces(). To bridge the APIs: en.asIterator() (Java 9) turns it into an Iterator (whose remove() throws UnsupportedOperationException), Collections.list(en) copies it into an ArrayList, and Collections.enumeration(collection) goes the other way for an old API.
7. What should you replace each legacy class with in new code, and which behavior differences should you check when migrating?
What should you replace each legacy class with in new code, and which behavior differences should you check when migrating?
Vector→ArrayList(in multithreaded code —CopyOnWriteArrayListorCollections.synchronizedList()). Check calls toaddElement(),elementAt(),setElementAt(): the argument order ofset()is reversed.Stack→ArrayDequewith aDequevariable type (for threads —ConcurrentLinkedDeque,LinkedBlockingDeque).peek()on an empty stack returnsnullinstead of throwingEmptyStackException,pop()throwsNoSuchElementException,nullelements are not allowed, and there is nosearch().Hashtable→HashMaporConcurrentHashMap.HashMapacceptsnull: code that used to catchNullPointerExceptionwill silently storenull;contains()→containsValue().Dictionary→ theMapinterface (ConcurrentMap): your own subclasses are rewritten asimplements Maporextends AbstractMap.Enumeration→Iteratorand for-each; third-party APIs are wrapped withasIterator().Propertiesstays: the JDK has no direct replacement for configuration files, butput()/get()are changed tosetProperty()/getProperty().
Declare the variable with the interface type — List<String> names = new ArrayList<>();, Deque<Integer> stack = new ArrayDeque<>(); — so the implementation can be changed in one line.