How to read a properties file in java in the original order [duplicate]
I need to read a properties file and generate a Properties class in Java. I do so by using:
Properties props = new Properties();
props.load(new FileInputStream(args[0]));
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
}
However, the properties returned by props.propertyName is no开发者_StackOverflow中文版t in the order of the original properties file. I understand that Properties are just old fashioned, non-generified Hashtables. I'm looking for a work around. Any idea? Thank you!
Example from www.java2s.com should solve your problem.
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
/**
* <a href="OrderedProperties.java.html"><b><i>View Source</i></b></a>
*
* @author Brian Wing Shun Chan
*
*/
public class OrderedProperties extends Properties {
public OrderedProperties() {
super ();
_names = new Vector();
}
public Enumeration propertyNames() {
return _names.elements();
}
public Object put(Object key, Object value) {
if (_names.contains(key)) {
_names.remove(key);
}
_names.add(key);
return super .put(key, value);
}
public Object remove(Object key) {
_names.remove(key);
return super .remove(key);
}
private Vector _names;
}
And your code will change to:
Properties props = new OrderedProperties();
props.load(new FileInputStream(args[0]));
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
}
You can extend Properties and delegate all map methods to a LinkedHashMap to retain the order. Here is an example (you may need to override some more methods):
public class LinkedProperties extends Properties{
private static final long serialVersionUID = 1L;
private Map<Object, Object> linkMap = new LinkedHashMap<Object,Object>();
@Override
public synchronized Object put(Object key, Object value){
return linkMap.put(key, value);
}
@Override
public synchronized boolean contains(Object value){
return linkMap.containsValue(value);
}
@Override
public boolean containsValue(Object value){
return linkMap.containsValue(value);
}
@Override
public synchronized Enumeration<Object> elements(){
throw new UnsupportedOperationException(
"Enumerations are so old-school, don't use them, "
+ "use keySet() or entrySet() instead");
}
@Override
public Set<Entry<Object, Object>> entrySet(){
return linkMap.entrySet();
}
@Override
public synchronized void clear(){
linkMap.clear();
}
@Override
public synchronized boolean containsKey(Object key){
return linkMap.containsKey(key);
}
}
Similar to one of the above, but w/out the overhead of maintaining our own list of values. All we have to do is maintain a separate ordered list of the keys, and provide a new "keys()" method.
public class SequencedProperties extends Properties {
private static final long serialVersionUID = -7032434592318855760L;
private List keyList = new ArrayList();
@Override
public synchronized Enumeration keys() {
return Collections.enumeration(keyList);
}
@Override
public synchronized Object put(Object key, Object value) {
if (! containsKey(key)) {
keyList.add(key);
}
return super.put(key, value);
}
@Override
public synchronized Object remove(Object key) {
keyList.remove(key);
return super.remove(key);
}
@Override
public synchronized void putAll(Map values) {
for (Object key : values.keySet()) {
if (! containsKey(key)) {
keyList.add(key);
}
}
super.putAll(values);
}
}
You may want to implement your own Properties class with similar functionalities.
It will not be possible for you to obtain the order since, as you already pointed out, it uses Hashtable
.
full implementation based on LinkedHashMap
import java.util.*;
import java.io.*;
/**
* Ordered properties implementation
*/
public class LinkedProperties extends Properties{
private static final long serialVersionUID = 1L;
private Map<Object, Object> linkMap = new LinkedHashMap<Object,Object>();
public void clear(){
linkMap.clear();
}
public boolean contains(Object value){
return linkMap.containsValue(value);
}
public boolean containsKey(Object key){
return linkMap.containsKey(key);
}
public boolean containsValue(Object value){
return linkMap.containsValue(value);
}
public Enumeration elements(){
throw new RuntimeException("Method elements is not supported in LinkedProperties class");
}
public Set entrySet(){
return linkMap.entrySet();
}
public boolean equals(Object o){
return linkMap.equals(o);
}
public Object get(Object key){
return linkMap.get(key);
}
public String getProperty(String key) {
Object oval = get(key); //here the class Properties uses super.get()
if(oval==null)return null;
return (oval instanceof String) ? (String)oval : null; //behavior of standard properties
}
public boolean isEmpty(){
return linkMap.isEmpty();
}
public Enumeration keys(){
Set keys=linkMap.keySet();
return Collections.enumeration(keys);
}
public Set keySet(){
return linkMap.keySet();
}
public void list(PrintStream out) {
this.list(new PrintWriter(out,true));
}
public void list(PrintWriter out) {
out.println("-- listing properties --");
for (Map.Entry e : (Set<Map.Entry>)this.entrySet()){
String key = (String)e.getKey();
String val = (String)e.getValue();
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
}
public Object put(Object key, Object value){
return linkMap.put(key, value);
}
public int size(){
return linkMap.size();
}
public Collection values(){
return linkMap.values();
}
//for test purpose only
public static void main(String[] arg)throws Exception{
Properties p0=new Properties();
Properties p1=new LinkedProperties();
p0.put("aaa","111");
p0.put("bbb","222");
p0.put("ccc","333");
p0.put("ddd","444");
p1.put("aaa","111");
p1.put("bbb","222");
p1.put("ccc","333");
p1.put("ddd","444");
System.out.println("\n--"+p0.getClass());
p0.list(System.out);
p0.store(System.out,"comments");
p0.storeToXML(System.out,"comments");
System.out.println(p0.toString());
System.out.println("\n--"+p1.getClass());
p1.list(System.out);
p1.store(System.out,"comments");
p1.storeToXML(System.out,"comments");
System.out.println(p1.toString());
}
}
Result:
--class java.util.Properties
-- listing properties --
bbb=222
aaa=111
ddd=444
ccc=333
#comments
#Wed Apr 10 08:55:42 EEST 2013
bbb=222
aaa=111
ddd=444
ccc=333
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>comments</comment>
<entry key="bbb">222</entry>
<entry key="aaa">111</entry>
<entry key="ddd">444</entry>
<entry key="ccc">333</entry>
</properties>
{bbb=222, aaa=111, ddd=444, ccc=333}
--class groovy.abi.LinkedProperties
-- listing properties --
aaa=111
bbb=222
ccc=333
ddd=444
#comments
#Wed Apr 10 08:55:42 EEST 2013
aaa=111
bbb=222
ccc=333
ddd=444
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>comments</comment>
<entry key="aaa">111</entry>
<entry key="bbb">222</entry>
<entry key="ccc">333</entry>
<entry key="ddd">444</entry>
</properties>
{aaa=111, bbb=222, ccc=333, ddd=444}
The fact they are represented as a Hashtable
under the hood means that their order is not kept in any fashion.
I'd suggest you "roll your own" properties reader if you're absolutely desperate for this functionality.
Proper implementation of keySet:
public class OrderedProperties extends Properties {
private Set<Object> keySet = new LinkedHashSet<Object>(100);
@Override
public Enumeration<Object> keys() {
return Collections.enumeration(keySet);
}
@Override
public Set<Object> keySet() {
return keySet;
}
@Override
public synchronized Object put(Object key, Object value) {
if (! keySet.contains(key)) {
keySet.add(key);
}
return super.put(key, value);
}
@Override
public synchronized Object remove(Object key) {
keySet.remove(key);
return super.remove(key);
}
@Override
public synchronized void putAll(Map values) {
for (Object key : values.keySet()) {
if (! containsKey(key)) {
keySet.add(key);
}
}
super.putAll(values);
}
}
Subclass Properties to memorize reading order and create an Enumeration that uses the ordered keys list ?
To solve the problem: "to execute classes based on the order in the properties file." I normally used one of 2 possibilities:
1 - use one property as a comma-separated list with the class-names or with the keys to the class definition
loadClasses = class-definition-A, class-definition-B, class-definition-C
or (useful if the "definition" consists of more than one property)
loadClasses = keyA, keyB, keyC
keyA = class-definition-A
keyB = class-definition-B
keyC = class-definition-C
2 - use a key followed by an index (counter). Read the keys in a loop until no value is found.
class1 = class-definition-A
class2 = class-definition-B
class3 = class-definition-C
精彩评论