如何在Java中按照原始顺序读取属性文件

16

我需要在Java中读取一个属性文件并生成一个Properties类。我使用以下代码实现:

Properties props = new Properties();
props.load(new FileInputStream(args[0]));
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
}
然而,props.propertyName返回的属性顺序并不是原始属性文件中的顺序。我知道Properties只是旧时代的非泛型哈希表。我正在寻找一种解决方法。有什么想法吗?谢谢!

有特定的原因导致顺序很重要吗? - Powerlord
1
我需要根据属性文件中的顺序执行类。 - wen
它实际上是泛型化的,作为Hashtable<Object,Object>(关于为什么是这样的问题在某个地方)。 - Tom Hawtin - tackline
+1 谢谢你替我问这个问题,我也遇到了完全相同的问题! :) - samsamara
public Map<Object, Object> getOrderedProperties(InputStream in) throws IOException{ Map<Object, Object> mp = new LinkedHashMap<>(); (new Properties(){ public synchronized Object put(Object key, Object value) { return mp.put(key, value); } }).load(in); return mp; } - Dharmendrasinh Chudasama
9个回答

18

你的问题可以通过从 www.java2s.com 获取的示例得到解决。

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;

}

你的代码将会变成:

Properties props = new OrderedProperties();
props.load(new FileInputStream(args[0]));
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
}

1
你可能需要考虑到这个问题可能被解释为我们想要读取一个非程序生成或非使用你的OrderedProperties类创建的原始文件。在这种情况下,所提供的解决方案并不是一个解决方案。 - H2ONaCl
1
@broiyan 这个解决方案应该能够适用于无论属性文件是如何创建的。 - YoK
这个非常好用。我使用它的方式如下: Properties properties = new OrderedProperties(); Map<String, String> propertyFileMap = new LinkedHashMap<>(); Enumeration<String> propertyKeyNames = (Enumeration<String>) properties.propertyNames(); while (propertyKeyNames.hasMoreElements()) { String propertyKey = propertyKeyNames.nextElement(); String propertyValue = properties.getProperty(propertyKey); propertyFileMap.put(propertyKey, propertyValue); } - Rahul Saini
这个继承自Properties的OrderedProperties应该是被接受的答案!! - Rahul Saini

13
你可以扩展属性并将所有映射方法委托给LinkedHashMap以保留顺序。这是一个示例(您可能需要覆盖更多的方法):
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);
    }

}

简单明了。通过在superlinkMap中存储值,可以使其更简单,并且只能覆盖修改和迭代类型操作。例如:put { linkedMap.put(key, value); return super.put(key, value); } - karmakaze
我建议您也覆盖size()toString()方法。 - To Kra
我只需要实际上覆盖更多的内容 :-),但还是谢谢你的启发。 - To Kra

8
类似于上面的方法,但是不需要维护自己的值列表。我们只需要维护一个单独的有序键列表,并提供一个新的“keys()”方法即可。

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);
    }
}

这个时候,使用 LinkedHashSet 而避免所有的 containsKey 怎么样? - Campa

6

你可能想要实现自己的Properties类,并具有类似的功能。 由于它使用了Hashtable,所以无法获取顺序。


3
基于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());
    }
}

结果:

--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}

只有这个对我起作用了。它按实际顺序读取属性文件。 - linuxeasy

2

它们被表示为Hashtable,意味着它们的顺序不以任何方式保留。

如果你非常需要这个功能,我建议你自己编写属性读取器。


3
使用Hashtable没有什么“under the hood”的内容 - Properties extends Hashtable<Object,Object>。 (注:意思是使用Properties类时,它实际上是通过继承Hashtable类来实现的,而并没有使用Hashtable类的隐藏特性或机制。) - Tom Hawtin - tackline

2

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);
  }
}

我认为在这里调用.contains()是不相关的,对吧?PS:对于非俄语读者:那个可怕的西里尔字母“правильной реализацией”意思是“正确的实现”! - Campa
你好!包含()很好用。可怕的西里尔字母替换为自然的英语 :) - Grigory Kislin
为什么load方法没有被覆盖重写? - Stephan

0
子类属性以记忆阅读顺序并创建使用有序键列表的枚举?

0
为了解决“根据属性文件中的顺序执行类”的问题,我通常使用以下两种可能性之一:
1- 使用一个属性作为逗号分隔的列表,其中包含类名或类定义的键 loadClasses = class-definition-A, class-definition-B, class-definition-C
或者(如果“定义”由多个属性组成,则很有用) loadClasses = keyA, keyB, keyC keyA = class-definition-A keyB = class-definition-B keyC = class-definition-C
2- 使用一个键后跟一个索引(计数器)。在循环中读取键,直到找不到值。
class1 = class-definition-A class2 = class-definition-B class3 = class-definition-C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接