35

我有一个属性文件,其中值的顺序很重要。我希望能够遍历属性文件并根据原始文件的顺序输出值。

但是,由于属性文件由不维护插入顺序的 Map 支持,如果我错了,请纠正我,迭代器以错误的顺序返回值

这是我正在使用的代码

Enumeration names = propfile.propertyNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    //do stuff
}

无论如何,如果没有编写我自己的自定义文件解析器,是否可以恢复属性?

4

15 回答 15

67

扩展java.util.Properties,覆盖两者put()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);
    }
}
于 2010-05-13T15:32:20.460 回答
11

不——地图本质上是“无序的”。

您可能创建自己的子类,Properties该子类覆盖setProperty和可能put,但它可能会变得非常特定于实现......Properties是不良封装的一个典型例子。当我上次写一个扩展版本(大约 10 年前!)时,它最终变得很糟糕,而且对Properties.

于 2009-08-21T14:45:37.110 回答
7

如果您可以更改属性名称,则可以在它们前面加上数字或其他可排序的前缀,然后对 Properties KeySet 进行排序。

于 2009-08-21T15:00:02.640 回答
7

工作示例:

Map<String,String> properties = getOrderedProperties(new FileInputStream(new File("./a.properties")));
properties.entrySet().forEach(System.out::println);

它的代码

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;
}
于 2017-12-15T12:18:37.413 回答
6

Dominique Laurent 的上述解决方案对我来说非常有用。我还添加了以下方法覆盖:

public Set<String> stringPropertyNames() {
    Set<String> set = new LinkedHashSet<String>();

    for (Object key : this.keys) {
        set.add((String)key);
    }

    return set;
}

可能不是最有效的,但它在我的 servlet 生命周期中只执行一次。

谢谢多米尼克!

于 2011-02-16T19:23:43.033 回答
5

Apache Commons Configuration可能会为您解决问题。我自己没有对此进行测试,但我检查了它们的来源,看起来属性键由 AbstractFileConfiguration 类中的 LinkedList 支持:

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();
    }
}
于 2009-08-21T15:30:22.560 回答
3

为了完整性...

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

我不认为返回集合的方法应该被覆盖,因为定义的集合不维护插入顺序

于 2013-02-14T12:36:18.690 回答
3

我将在此线程中添加一个更著名的 YAEOOJP(有序 Java 属性的另一个示例),因为似乎没有人会关心您可以提供给属性的默认属性。

@see http://docs.oracle.com/javase/tutorial/essential/environment/properties.html

那是我的课:肯定不是 10 16 % 符合任何可能的情况,但这对于我现在有限的愚蠢目的来说很好。感谢任何进一步的纠正意见,以便更大的善能受益。

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;
}
于 2015-04-17T08:16:23.613 回答
3
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));
}

这保持了属性文件的顺序

于 2016-04-18T10:29:10.757 回答
2

如果要将属性导出为 XML,则还必须覆盖 keySet():

public Set<Object> keySet() { return keys; }

于 2010-08-18T20:34:48.653 回答
2

有关允许以明确定义的顺序读取/写入属性文件的完整实现,请参阅https://github.com/etiennestuder/java-ordered-properties 。

OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
于 2014-12-12T16:37:46.630 回答
2

正如我所看到的,Properties是非常必然的Hashtable。我建议阅读它以了解LinkedHashMap. 为此,您只需要覆盖一个方法,Object put(Object key, Object value),而忽略作为Properties键/值容器:

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

用法:

LinkedHashMap<String, String> props = new LinkedHashMap<>();
try (InputStream inputStream = new FileInputStream(file)) {
    new InOrderPropertiesLoader<>(props).load(inputStream);
}
于 2016-08-11T15:26:11.043 回答
2

某些答案中,假设从文件中读取的属性被放入Properties(通过调用put)的实例中,以便它们出现在文件中。虽然这通常是它的行为方式,但我看不到这种顺序的任何保证。

恕我直言:最好逐行读取文件(以保证顺序),而不是将 Properties 类用作单个属性行的解析器,最后将其存储在一些有序的 Collection 中,例如LinkedHashMap.

这可以这样实现:

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;

  }

请注意,上面发布的方法采用InputStream之后应该关闭的方法(当然,重写它以仅将文件作为参数是没有问题的)。

于 2016-12-09T10:56:14.440 回答
2

对于最近阅读此主题的人:只需使用 org.apache.commons:commons-configuration2 中的类 PropertiesConfiguration。我已经测试过它保持属性排序(因为它在内部使用 LinkedHashMap)。正在做:

`
    PropertiesConfiguration properties = new PropertiesConfiguration();
    properties.read(new FileReader("/some/path));
    properties.write(new FileWriter("/some/other/path"));
`

仅删除尾随空格和不必要的转义。

于 2019-07-26T10:11:32.150 回答
-1

另一种方法是使用 LinkedHashMap 编写自己的属性文件,这是我使用的:

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

}

要使用:

    OrderedProperties o = OrderedProperties.getInstance("/project.properties");
    System.out.println(o.getProperty("test"));

示例属性文件(在本例中为 project.properties):

test=test2
于 2013-12-18T14:58:46.477 回答