In Python, the defaultdict class provides a convenient way to create a mapping from key -> [list of values], in the following example,
from collections import defaultdict
d = defaultdict(list)
d[1].append(2)
d[1].append(3)
# d is now {1: [2, 3]}
Is there an equivalent to this in Java?
There is nothing that gives the behaviour of default dict out of the box. However creating your own default dict in Java would not be that difficult.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DefaultDict<K, V> extends HashMap<K, V> {
Class<V> klass;
public DefaultDict(Class klass) {
this.klass = klass;
}
#Override
public V get(Object key) {
V returnValue = super.get(key);
if (returnValue == null) {
try {
returnValue = klass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
this.put((K) key, returnValue);
}
return returnValue;
}
}
This class could be used like below:
public static void main(String[] args) {
DefaultDict<Integer, List<Integer>> dict =
new DefaultDict<Integer, List<Integer>>(ArrayList.class);
dict.get(1).add(2);
dict.get(1).add(3);
System.out.println(dict);
}
This code would print: {1=[2, 3]}
In most common cases where you want a defaultdict, you'll be even happier with a properly designed Multimap or Multiset, which is what you're really looking for. A Multimap is a key -> collection mapping (default is an empty collection) and a Multiset is a key -> int mapping (default is zero).
Guava provides very nice implementations of both Multimaps and Multisets which will cover almost all use cases.
But (and this is why I posted a new answer) with Java 8 you can now replicate the remaining use cases of defaultdict with any existing Map.
getOrDefault(), as the name suggests, returns the value if present, or returns a default value. This does not store the default value in the map.
computeIfAbsent() computes a value from the provided function (which could always return the same default value) and does store the computed value in the map before returning.
If you want to encapsulate these calls you can use Guava's ForwardingMap:
public class DefaultMap<K, V> extends ForwardingMap<K, V> {
private final Map<K, V> delegate;
private final Supplier<V> defaultSupplier;
/**
* Creates a map which uses the given value as the default for <i>all</i>
* keys. You should only use immutable values as a shared default key.
* Prefer {#link #create(Supplier)} to construct a new instance for each key.
*/
public static DefaultMap<K, V> create(V defaultValue) {
return create(() -> defaultValue);
}
public static DefaultMap<K, V> create(Supplier<V> defaultSupplier) {
return new DefaultMap<>(new HashMap<>(), defaultSupplier);
}
public DefaultMap<K, V>(Map<K, V> delegate, Supplier<V> defaultSupplier) {
this.delegate = Objects.requireNonNull(delegate);
this.defaultSupplier = Objects.requireNonNull(defaultSupplier);
}
#Override
public V get(K key) {
return delegate().computeIfAbsent(key, k -> defaultSupplier.get());
}
}
Then construct your default map like so:
Map<String, List<String>> defaultMap = DefaultMap.create(ArrayList::new);
in addition to apache collections, check also google collections:
A collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.
In Java 8+ you can use:
map.computeIfAbsent(1, k -> new ArrayList<Integer>()).add(2);
You can use MultiMap from Apache Commons.
Using just the Java runtime library you could use a HashMap and add an ArrayList to hold your values when the key does not exist yet or add the value to the list when the key does exist.
The solution from #tendayi-mawushe did not work for me with Primitive types (e.g. InstantiationException Integer), here is one implementation that works with Integer, Double, Float. I often use Maps with these and added static constructors for conveninence
import java.util.HashMap;
import java.util.Map;
/** Simulate the behaviour of Python's defaultdict */
public class DefaultHashMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
private final Class<V> cls;
private final Number defaultValue;
#SuppressWarnings({ "rawtypes", "unchecked" })
public DefaultHashMap(Class factory) {
this.cls = factory;
this.defaultValue = null;
}
public DefaultHashMap(Number defaultValue) {
this.cls = null;
this.defaultValue = defaultValue;
}
#SuppressWarnings("unchecked")
#Override
public V get(Object key) {
V value = super.get(key);
if (value == null) {
if (defaultValue == null) {
try {
value = cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
} else {
value = (V) defaultValue;
}
this.put((K) key, value);
}
return value;
}
public static <T> Map<T, Integer> intDefaultMap() {
return new DefaultHashMap<T, Integer>(0);
}
public static <T> Map<T, Double> doubleDefaultMap() {
return new DefaultHashMap<T, Double>(0d);
}
public static <T> Map<T, Float> floatDefaultMap() {
return new DefaultHashMap<T, Float>(0f);
}
public static <T> Map<T, String> stringDefaultMap() {
return new DefaultHashMap<T, String>(String.class);
}
}
And a test, for good manners:
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Test;
public class DefaultHashMapTest {
#Test
public void test() {
Map<String, List<String>> dm = new DefaultHashMap<String, List<String>>(
ArrayList.class);
dm.get("nokey").add("one");
dm.get("nokey").add("two");
assertEquals(2, dm.get("nokey").size());
assertEquals(0, dm.get("nokey2").size());
}
#Test
public void testInt() {
Map<String, Integer> dm = DefaultHashMap.intDefaultMap();
assertEquals(new Integer(0), dm.get("nokey"));
assertEquals(new Integer(0), dm.get("nokey2"));
dm.put("nokey", 3);
assertEquals(new Integer(0), dm.get("nokey2"));
dm.put("nokey3", 3);
assertEquals(new Integer(3), dm.get("nokey3"));
}
#Test
public void testString() {
Map<String, String> dm = DefaultHashMap.stringDefaultMap();
assertEquals("", dm.get("nokey"));
dm.put("nokey1", "mykey");
assertEquals("mykey", dm.get("nokey1"));
}
}
I wrote the library Guavaberry containing such data structure: DefaultHashMap.
It is highly tested and documented. You can find it and integrate it pretty easily via Maven Central.
The main advatage is that it uses lambda to define the factory method. So, you can add an arbitrarly defined instance of a class (instead of relying on the existence of the default constructor):
DefaultHashMap<Integer, List<String>> map = new DefaultHashMap(() -> new ArrayList<>());
map.get(11).add("first");
I hope that can be of help.
Related
I have a BiDiMap class. How can I make it generic, by accepting not only String but also Object type of objects as input parameters, with keeping all the original functions working. For example I'd like to be able to use function put() with Object, Object as input parameters instead of String, String. I'd like to change all the input parameters and returning values of String type to Object types.
package MyBiDiMap;
import java.util.HashMap;
import java.util.Map;
public class BiDiMap {
private Map<String, String> keyValue;
private Map<String, String> valueKey;
public BiDiMap() {
this.keyValue = new HashMap<>();
this.valueKey = new HashMap<>();
}
private BiDiMap(Map<String, String> keyValue,
Map<String, String> valueKey) {
this.keyValue = keyValue;
this.valueKey = valueKey;
}
public void put(String key, String value) {
if (this.keyValue.containsKey(key)
|| this.valueKey.containsKey(value)) {
this.remove(key);
this.removeInverse(value);
}
this.keyValue.put(key, value);
this.valueKey.put(value, key);
}
public String get(String key) {
return this.keyValue.get(key);
}
public String getInverse(String value) {
return this.valueKey.get(value);
}
public void remove(String key) {
String value = this.keyValue.remove(key);
this.valueKey.remove(value);
}
public void removeInverse(String value) {
String key = this.valueKey.remove(value);
this.keyValue.remove(key);
}
public int size() {
return this.keyValue.size();
}
public BiDiMap getInverse() {
return new BiDiMap(this.valueKey, this.keyValue);
}
}
The answer is pretty simple: by introducing two generic types, named K and V on your class and by then vigorously replacing all occurance of String with K (where your key type should be used), and similarly with V where values are required.
In other words: don't use specific types when declaring the two maps, but in all places, use the new generic types you added on class level.
I am using org.apache.commons.lang3.text.StrSubstitutor to parse a String. I have it setup similar to this:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(killer) killed &(target)!");
This no longer works if I write the keys in different cases:
"Information: &(KILLER) killed &(TARGET)!"
Is there a way of making the keys for the String Substitutor case-insensitive?
I cannot use toLowerCase() because I only want the keys to be case-insensitive.
StrSubstitutor has a constructor that takes an instance of StrLookup. You can create an implementation of StrLookup that lowercases the keys its looking for before actually looking for them.
Here's how it looks like:
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map;
CaseInsensitiveStrLookup(final Map<String, V> map) {
this.map = map;
}
#Override
public String lookup(final String key) {
String lowercaseKey = key.toLowerCase(); //lowercase the key you're looking for
if (map == null) {
return null;
}
final Object obj = map.get(lowercaseKey);
if (obj == null) {
return null;
}
return obj.toString();
}
}
Using this StrLookup implementation you don't need to worry about what kind of Map you're passing to the constructor.
The following test case returns succesfully, using the above implementation:
import org.apache.commons.lang3.text.StrSubstitutor;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
#Test
public class TestClass {
#Test
public void test() {
Map<String, String> messageValues = new HashMap<String, String>();
messageValues.put("killer", "Johnson");
messageValues.put("target", "Quagmire");
StrSubstitutor sub = new StrSubstitutor(new CaseInsensitiveStrLookup<String>(messageValues), "&(", ")", '\\');
String format2 = sub.replace("Information: &(killer) killed &(target)!");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
Assert.assertEquals(format, "Information: Johnson killed Quagmire!");
Assert.assertEquals(format2, "Information: Johnson killed Quagmire!");
}
}
You don't need to write a custom class. Assuming you can live with the log(n) access times, just use a case-insensitive TreeMap.
public static void main(String[] args) {
Map<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put("foo", "bar");
StrSubstitutor sub = new StrSubstitutor(m);
String s = sub.replace("${FOO}");
System.out.println(s);
} // prints "bar"
I think this case-insensitive map would work:
import java.util.HashMap;
import java.util.Map;
public class CaseMap<V> extends HashMap<String, V> {
public CaseMap() {
}
public CaseMap(int capacity) {
super(capacity);
}
public CaseMap(int capacity, float loadFactor) {
super(capacity, loadFactor);
}
public CaseMap(Map<String, ? extends V> map) {
putAll(map);
}
public V put(String key, V value) {
return super.put(key.toUpperCase(), value);
}
public V get(Object key) {
if (!(key instanceof String)) return null;
return super.get(((String)key).toUpperCase());
}
}
If you don't control the creation of the messageValues map, you could build a CaseMap from it like this:
CaseMap<String> caseFreeMessageValues = new CaseMap<String>(messageValues);
And then build your StrSubstitutor like this:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
You might want to think about other methods of Map that should be overridden as well, such as containsKey.
In case you need flexibility with both the Map and the Tokens being case insensitive AND you are not in control of the map being built you can use something like this.
String replaceTokens(String str, Map<String, String> messageValues) {
if(tokenToValue == null || tokenToValue.size() < 1) return str;
StrSubstitutor caseInsensitiveTokenReplacer = new StrSubstitutor(new CaseInsensitiveStrLookup<>(messageValues),
"&(", ")", '\\');
return caseInsensitiveTokenReplacer.replace(str);
}
StrLookup Implementation
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map = new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER);
public CaseInsensitiveStrLookup(final Map<String, V> map) throws NullValueKeyNotSupported {
if(map.containsKey(null)) throw new Exception(); // Dont want to support null
this.map.putAll(map);
}
#Override
public String lookup(final String key) {
V value = map.get(key);
if(value == null) return null;
return value.toString();
}}
This question already has answers here:
java collection that has key/value pair, and is ordered according to insert order
(4 answers)
Closed 10 years ago.
I am working on a project and I need to store key value pairs (one-to-one mapping) in an ordered fashion. Then I should be able to retrieve the key using the value and value using the key. I have looked at Maps, Sets and Hash Tables, but they aren't ordered.
Also, though trivial, it would be great if we could DS retrieve the keys and values at once i.e., the interface supports such functions.
EDIT: The keys and values are all unique. Maintaining the inserted order is good enough.
Notice that you don't define what counts as "ordered". A LinkedHashMap enables iterating over the keys (and therefore values) in insertion-order. Conversely, a TreeMap lets you specify a sort order with a comparator, and ensures all items added to the map are stored in sorted order. 99 times out of 100, one of these classes should be all you need. Alternatively, Google's Guava project has several very nice BiMap implementations that you may find fits your needs.
I strongly caution you: if you think you need more than what these classes can provide, you are likely over-engineering your problem.
For reasons I can't fully justify, I implemented a proper UniqueOrderedBiMap for you, which is compatible with the Java Collections framework and all implemented functions run efficiently. You can use whatever underlying map you see fit (including an un-ordered map, if you really wanted) and keys and values are always unique. Notice that it is a very thin wrapper around a LinkedHashMap, because that's all you need, a LinkedHashMap with extra checks to ensure Values remain unique.
For the curious, check this answers revision history for a UniqueOrderedMap which lacks the getKey() and removeKey() methods, but more properly implements the Map interface, and only needs a HashSet, rather than a HashMap, to store the known values.
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class UniqueOrderedBiMap<K, V>implements Map<K, V> {
private Map<K, V> orderedMap;
private HashMap<V, K> valueMap;
public UniqueOrderedBiMap() {
this(new LinkedHashMap<K,V>());
}
public UniqueOrderedBiMap(Map<K, V> underlyingMap) {
orderedMap = underlyingMap;
valueMap = new HashMap<V, K>(orderedMap.size());
for(Map.Entry<K, V> e : orderedMap.entrySet()) {
if(!valueMap.containsKey(e.getValue())) { // Duplicate value
// could instead fail softly by removing the associated item from the map, but this seems cleaner/clearer.
// generally this constructor should be passed an empty map anyways
throw new IllegalArgumentException("Duplicate value "+e.getValue()+" found in underlying map.");
}
valueMap.put(e.getValue(), e.getKey());
}
}
#Override
public int size() {
return orderedMap.size();
}
#Override
public boolean isEmpty() {
return orderedMap.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return orderedMap.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
// more efficient than iterating over the map
return valueMap.containsKey(value);
}
#Override
public V get(Object key) {
return orderedMap.get(key);
}
public K getKey(V value) {
return valueMap.get(value);
}
// Likely want to implement a forcePut(K, V) method like Guava's BiMaps do
#Override
public V put(K key, V value) {
if(valueMap.containsKey(value)) {
throw new IllegalArgumentException("Cannot insert non-unique value "+value);
}
V ret = orderedMap.put(key, value);
valueMap.remove(ret);
valueMap.put(value, key);
return ret;
}
#Override
public V remove(Object key) {
V ret = orderedMap.remove(key);
valueMap.remove(ret);
return ret;
}
public K removeKey(V value) {
K ret = valueMap.remove(value);
orderedMap.remove(ret);
return ret;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
// Existing Map implementation's putAll have some optimizations we
// could take advantage of, but this isn't unreasonable for a first pass
for(Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
#Override
public void clear() {
orderedMap.clear();
valueMap.clear();
}
#Override
public Set<K> keySet() {
return orderedMap.keySet();
}
#Override
public Collection<V> values() {
return orderedMap.values();
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return orderedMap.entrySet();
}
#Override
public boolean equals(Object o) {
if(o instanceof UniqueOrderedBiMap) {
UniqueOrderedBiMap<?,?> map = (UniqueOrderedBiMap<?,?>)o;
return orderedMap.equals(map.orderedMap);
}
return false;
}
#Override
public int hashCode() {
return orderedMap.hashCode();
}
#Override public String toString() {
return orderedMap.toString();
}
public static void main(String[] args) {
String[] names = { "Marcus", "Jim", "Tom", "Sam" };
String[] grades = { "A", "B", "D", "F" };
UniqueOrderedBiMap<String,String> insertionMap = new UniqueOrderedBiMap<>();
UniqueOrderedBiMap<String,String> sortedMap = new UniqueOrderedBiMap<>(new TreeMap<String,String>());
for(int i = 0; i < names.length; i++) {
insertionMap.put(names[i], grades[i]);
sortedMap.put(names[i], grades[i]);
}
// Poor man's assert
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=D, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=D}"));
insertionMap.put("Tom", "C");
sortedMap.put("Tom", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=C, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=C}"));
try {
insertionMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
try {
sortedMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
insertionMap.remove("Tom");
sortedMap.remove("Tom");
insertionMap.put("Sam", "C");
sortedMap.put("Sam", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=C}"));
insertionMap.removeKey("A");
sortedMap.removeKey("A");
System.out.println(insertionMap.toString().equals("{Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Sam=C}"));
}
}
If you can use third party libraries then consider using an ImmutableBiMap. Its a Guava Collection class that provides
User specified iteration order
Normal mapping from keys to values and inverse mapping from values to keys
The one consideration is that once created the map is immutable and cannot be modified.
LinkedHashMap should be suitable for you. Read through the link
You will need two LinkedHashMap. You can create custom class that internally uses two LinkedHashMap. One for mapping keys to value and another one for mapping values to key.
I am trying to write some general code to do the following. Given two kinds of "operations", (a) validation (eg. input: object & context -> output: boolean), and (b) transformation (eg. input: object_A, context -> output: object_B) -objects of any type-.
I want to be able to build chains of "operations", in which an input object and its context can be submitted through (eg. to validate and transform the object). Returning immediately if the object is "invalid" and being able to get the transformed object if it finished "valid".
Idea is that "validations" and "transformations" can be "plugable" functions that other people write and assemble in a chain (eg. they build chains and submit objects through them).
I managed to do the following code, which compiles and seems to work. However, I'm not an expert on generics and would like to hear feedback about possible pitfalls, enhancements, or even maybe some other better/easier approach to the problem. Thanks in advance.
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
interface Operation<T, U, V> {
U execute(T a, V context);
}
abstract class Validation<T, V> implements Operation<T, Boolean, V> {
#Override
public Boolean execute(T a, V context) {
return executeValidation(a, context);
}
public abstract Boolean executeValidation(T a, V context);
}
abstract class Transformation<T, U, V> implements Operation<T, U, V> {
#Override
public U execute(T a, V context) {
return executeTransformation(a, context);
}
public abstract U executeTransformation(T a, V context);
}
class OperationsChain {
List<Operation<Object, Object, Object>> operations = new ArrayList<Operation<Object, Object, Object>>();
Object currentObj;
public <T, V> Boolean run(T a, V context) {
Boolean valid = false;
currentObj = a;
for (Operation<Object, Object, Object> operation : operations) {
if (operation instanceof Validation) {
valid = (Boolean) operation.execute(currentObj, context);
} else if (operation instanceof Transformation) {
currentObj = operation.execute(currentObj, context);
}
if (!valid) {
break;
}
}
return valid;
}
#SuppressWarnings("unchecked")
public <T, U, V> void addOperation(Operation<T, U, V> operation) {
operations.add((Operation<Object, Object, Object>) operation);
}
public Object getCurrentObject() {
return currentObj;
}
}
class ValidationOne extends Validation<MapObject, Map<String, Object>> {
public Boolean executeValidation(MapObject a, Map<String, Object> context) {
if (context.containsKey("validation 1")) {
return (Boolean) context.get("validation 1");
} else {
return false;
}
}
}
class ValidationTwo extends Validation<MapObject, Map<String, Object>> {
public Boolean executeValidation(MapObject a, Map<String, Object> context) {
if (context.containsKey("validation 2")) {
return (Boolean) context.get("validation 2");
} else {
return false;
}
}
}
class TransformationOne extends Transformation<MapObject, MapObject, Map<String, Object>> {
public MapObject executeTransformation(MapObject a, Map<String, Object> context) {
if (context.containsKey("transformation 1")) {
a.addField("data", (String) context.get("transformation 1"));
}
return a;
}
}
class MapObject {
Map<String, String> fields = new HashMap<String, String>();
public void addField(String key, String value) {
fields.put(key, value);
}
public String getField(String key, String value) {
if (fields.containsKey(key)) {
return fields.get(key);
} else {
return null;
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : fields.entrySet()) {
sb.append(entry.getKey());
sb.append(": ");
sb.append(entry.getValue());
sb.append("\n");
}
return sb.toString();
}
}
class OperationsChainDriver {
public static void main(String[] args) {
OperationsChain oc = new OperationsChain();
oc.addOperation(new ValidationOne());
oc.addOperation(new TransformationOne());
oc.addOperation(new ValidationTwo());
oc.addOperation(new TransformationOne());
Map<String, Object> context = new HashMap<String, Object>();
context.put("validation 1", true);
context.put("validation 2", false);
context.put("transformation 1", "aloha");
MapObject mapObject = new MapObject();
mapObject.addField("field 1", "hello");
Boolean result = oc.run(mapObject, context);
if (result == true) {
System.out.println("valid\n"+oc.getCurrentObject().toString());
} else {
System.out.println("invalid\n"+oc.getCurrentObject().toString());
}
}
}
Generics are about type safety - not having to cast, because as you surely know casts are risks proved runtime. You have a very generic design yet get very concrete to and the like and have to cast a lot - this shouldn't happen since it defeats the reason to use generics at all.
As as side note: why not give an operation a method isValid that has always a return type of Boolean, a transformation can fail, too, so yo don't have to make a difference between validation and transformation. Or let it put a value in a context - the operation could know its context and could use it without casts. An operation chain could know its context and could get the results without casts.
Anyway - as long as you code has casts you are still not finished with it.
This kind of task is one that I think a functional language would be ideal for, e.g. Scala (which runs on the JVM and is perfect for interoperating with Java code), or Haskell (which doesn't run on the JVM, but has some other advantages).
OK, I understand if you don't want to learn a new programming language. But one of the key advantages would be that your code should be shorter and easier to read and reason about.
I have a properties file where the order of the values is important. I want to be able to iterate through the properties file and output the values based on the order of the original file.
However, since the Properties file is backed by, correct me if I'm wrong, a Map that does not maintain insertion order, the iterator returns the values in the wrong order.
Here is the code I'm using
Enumeration names = propfile.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
//do stuff
}
Is there anyway to get the Properties back in order short of writting my own custom file parser?
Extend java.util.Properties, override both put() and keys():
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.HashMap;
public class LinkedProperties extends Properties {
private final HashSet<Object> keys = new LinkedHashSet<Object>();
public LinkedProperties() {
}
public Iterable<Object> orderedKeys() {
return Collections.list(keys());
}
public Enumeration<Object> keys() {
return Collections.<Object>enumeration(keys);
}
public Object put(Object key, Object value) {
keys.add(key);
return super.put(key, value);
}
}
Nope - maps are inherently "unordered".
You could possibly create your own subclass of Properties which overrode setProperty and possibly put, but it would probably get very implementation-specific... Properties is a prime example of bad encapsulation. When I last wrote an extended version (about 10 years ago!) it ended up being hideous and definitely sensitive to the implementation details of Properties.
If you can alter the property names your could prefix them with a numeral or other sortable prefix and then sort the Properties KeySet.
Working example :
Map<String,String> properties = getOrderedProperties(new FileInputStream(new File("./a.properties")));
properties.entrySet().forEach(System.out::println);
Code for it
public Map<String, String> getOrderedProperties(InputStream in) throws IOException{
Map<String, String> mp = new LinkedHashMap<>();
(new Properties(){
public synchronized Object put(Object key, Object value) {
return mp.put((String) key, (String) value);
}
}).load(in);
return mp;
}
Dominique Laurent's solution above works great for me. I also added the following method override:
public Set<String> stringPropertyNames() {
Set<String> set = new LinkedHashSet<String>();
for (Object key : this.keys) {
set.add((String)key);
}
return set;
}
Probably not the most efficient, but it's only executed once in my servlet lifecycle.
Thanks Dominique!
Apache Commons Configuration might do the trick for you. I haven't tested this myself, but I checked their sources and looks like property keys are backed by LinkedList in AbstractFileConfiguration class:
public Iterator getKeys()
{
reload();
List keyList = new LinkedList();
enterNoReload();
try
{
for (Iterator it = super.getKeys(); it.hasNext();)
{
keyList.add(it.next());
}
return keyList.iterator();
}
finally
{
exitNoReload();
}
}
I'll add one more famous YAEOOJP (Yet Another Example Of Ordered Java Properties) to this thread because it seems nobody could ever care less about default properties which you can feed to your properties.
#see http://docs.oracle.com/javase/tutorial/essential/environment/properties.html
That's my class: surely not 1016% compliant with any possible situation, but that is fine for my limited dumb purposes right now. Any further comment for correction is appreciated so the Greater Good can benefit.
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Remember javadocs >:o
*/
public class LinkedProperties extends Properties {
protected LinkedProperties linkedDefaults;
protected Set<Object> linkedKeys = new LinkedHashSet<>();
public LinkedProperties() { super(); }
public LinkedProperties(LinkedProperties defaultProps) {
super(defaultProps); // super.defaults = defaultProps;
this.linkedDefaults = defaultProps;
}
#Override
public synchronized Enumeration<?> propertyNames() {
return keys();
}
#Override
public Enumeration<Object> keys() {
Set<Object> allKeys = new LinkedHashSet<>();
if (null != defaults) {
allKeys.addAll(linkedDefaults.linkedKeys);
}
allKeys.addAll(this.linkedKeys);
return Collections.enumeration(allKeys);
}
#Override
public synchronized Object put(Object key, Object value) {
linkedKeys.add(key);
return super.put(key, value);
}
#Override
public synchronized Object remove(Object key) {
linkedKeys.remove(key);
return super.remove(key);
}
#Override
public synchronized void putAll(Map<?, ?> values) {
for (Object key : values.keySet()) {
linkedKeys.add(key);
}
super.putAll(values);
}
#Override
public synchronized void clear() {
super.clear();
linkedKeys.clear();
}
private static final long serialVersionUID = 0xC00L;
}
In the interest of completeness ...
public class LinkedProperties extends Properties {
private final LinkedHashSet<Object> keys = new LinkedHashSet<Object>();
#Override
public Enumeration<?> propertyNames() {
return Collections.enumeration(keys);
}
#Override
public synchronized Enumeration<Object> elements() {
return Collections.enumeration(keys);
}
public Enumeration<Object> keys() {
return Collections.enumeration(keys);
}
public Object put(Object key, Object value) {
keys.add(key);
return super.put(key, value);
}
#Override
public synchronized Object remove(Object key) {
keys.remove(key);
return super.remove(key);
}
#Override
public synchronized void clear() {
keys.clear();
super.clear();
}
}
I dont think the methods returning set should be overridden as a set by definition does not maintain insertion order
Map<String, String> mapFile = new LinkedHashMap<String, String>();
ResourceBundle bundle = ResourceBundle.getBundle(fileName);
TreeSet<String> keySet = new TreeSet<String>(bundle.keySet());
for(String key : keySet){
System.out.println(key+" "+bundle.getString(key));
mapFile.put(key, bundle.getString(key));
}
This persist the order of property file
You must override also keySet() if you want to export Properties as XML:
public Set<Object> keySet() {
return keys;
}
See https://github.com/etiennestuder/java-ordered-properties for a complete implementation that allows to read/write properties files in a well-defined order.
OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
In some answers it is assumed that properties read from file are put to instance of Properties (by calls to put) in order they appear they in file. While this is in general how it behaves I don't see any guarantee for such order.
IMHO: it is better to read the file line by line (so that the order is guaranteed), than use the Properties class just as a parser of single property
line and finally store it in some ordered Collection like LinkedHashMap.
This can be achieved like this:
private LinkedHashMap<String, String> readPropertiesInOrderFrom(InputStream propertiesFileInputStream)
throws IOException {
if (propertiesFileInputStream == null) {
return new LinkedHashMap(0);
}
LinkedHashMap<String, String> orderedProperties = new LinkedHashMap<String, String>();
final Properties properties = new Properties(); // use only as a parser
final BufferedReader reader = new BufferedReader(new InputStreamReader(propertiesFileInputStream));
String rawLine = reader.readLine();
while (rawLine != null) {
final ByteArrayInputStream lineStream = new ByteArrayInputStream(rawLine.getBytes("ISO-8859-1"));
properties.load(lineStream); // load only one line, so there is no problem with mixing the order in which "put" method is called
final Enumeration<?> propertyNames = properties.<String>propertyNames();
if (propertyNames.hasMoreElements()) { // need to check because there can be empty or not parsable line for example
final String parsedKey = (String) propertyNames.nextElement();
final String parsedValue = properties.getProperty(parsedKey);
orderedProperties.put(parsedKey, parsedValue);
properties.clear(); // make sure next iteration of while loop does not access current property
}
rawLine = reader.readLine();
}
return orderedProperties;
}
Just note that the method posted above takes an InputStream which should be closed afterwards (of course there is no problem to rewrite it to take just a file as an argument).
As I see it, Properties is to much bound to Hashtable. I suggest reading it in order to a LinkedHashMap. For that you'll only need to override a single method, Object put(Object key, Object value), disregarding the Properties as a key/value container:
public class InOrderPropertiesLoader<T extends Map<String, String>> {
private final T map;
private final Properties properties = new Properties() {
public Object put(Object key, Object value) {
map.put((String) key, (String) value);
return null;
}
};
public InOrderPropertiesLoader(T map) {
this.map = map;
}
public synchronized T load(InputStream inStream) throws IOException {
properties.load(inStream);
return map;
}
}
Usage:
LinkedHashMap<String, String> props = new LinkedHashMap<>();
try (InputStream inputStream = new FileInputStream(file)) {
new InOrderPropertiesLoader<>(props).load(inputStream);
}
For those who read this topic recently:
just use class PropertiesConfiguration from org.apache.commons:commons-configuration2.
I've tested that it keeps properties ordering (because it uses LinkedHashMap internally).
Doing:
`
PropertiesConfiguration properties = new PropertiesConfiguration();
properties.read(new FileReader("/some/path));
properties.write(new FileWriter("/some/other/path"));
`
only removes trailing whitespace and unnecessary escapes.
For Kotlin users, here's a basic example that's functional for write operations. The order is simply determined by the order of your calls to setProperty(k,v).
Per Kotlin's documentation for MutableMap and MutableSet, they both:
preserve the entry iteration order.
Not all use cases are covered.
class OrderedProperties: Properties() {
private val orderedMap = mutableMapOf<Any, Any>()
override val entries: MutableSet<MutableMap.MutableEntry<Any, Any>>
get() = Collections.synchronizedSet(orderedMap.entries)
#Synchronized
override fun put(key: Any?, value: Any?): Any? {
key ?: return null
value ?: return null
orderedMap[key] = value
return orderedMap
}
override fun setProperty(key: String?, value: String?): Any? {
return this.put(key, value)
}
}
An alternative is just to write your own properties file using LinkedHashMap, here is what I use :
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
public class OrderedProperties {
private static Map<String, String> properties = new LinkedHashMap<String, String>();
private static OrderedProperties instance = null;
private OrderedProperties() {
}
//The propertyFileName is read from the classpath and should be of format : key=value
public static synchronized OrderedProperties getInstance(String propertyFileName) {
if (instance == null) {
instance = new OrderedProperties();
readPropertiesFile(propertyFileName);
}
return instance;
}
private static void readPropertiesFile(String propertyFileName){
LineIterator lineIterator = null;
try {
//read file from classpath
URL url = instance.getClass().getResource(propertyFileName);
lineIterator = FileUtils.lineIterator(new File(url.getFile()), "UTF-8");
while (lineIterator.hasNext()) {
String line = lineIterator.nextLine();
//Continue to parse if there are blank lines (prevents IndesOutOfBoundsException)
if (!line.trim().isEmpty()) {
List<String> keyValuesPairs = Arrays.asList(line.split("="));
properties.put(keyValuesPairs.get(0) , keyValuesPairs.get(1));
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
lineIterator.close();
}
}
public Map<String, String> getProperties() {
return OrderedProperties.properties;
}
public String getProperty(String key) {
return OrderedProperties.properties.get(key);
}
}
To use :
OrderedProperties o = OrderedProperties.getInstance("/project.properties");
System.out.println(o.getProperty("test"));
Sample properties file (in this case project.properties) :
test=test2