我有一个属性文件,其中值的顺序很重要。我希望能够遍历属性文件并根据原始文件的顺序输出值。
但是,由于属性文件是后备的,如果我错了,请更正我,不保持插入顺序的Map,迭代器以错误的顺序返回值。
这是我正在使用的代码
Enumeration names = propfile.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
//do stuff
}
无论如何都要恢复属性,而不是写我自己的自定义文件解析器吗?
答案 0 :(得分:66)
扩展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);
}
}
答案 1 :(得分:11)
不 - 地图固有地“无序”。
你可能可能创建自己的Properties
子类,它会覆盖setProperty
,可能会put
,但它可能会特定于实现... Properties
是错误封装的主要示例。当我上次写一个扩展版本(大约10年前!)时,它最终变得丑陋,并且对Properties
的实现细节非常敏感。
答案 2 :(得分:6)
如果您可以更改属性名称,则可以使用数字或其他可排序前缀作为前缀,然后对属性键集进行排序。
答案 3 :(得分: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生命周期中执行过一次。
感谢Dominique!
答案 4 :(得分:5)
工作示例:
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;
}
答案 5 :(得分:4)
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();
}
}
答案 6 :(得分: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();
}
}
我不认为返回集的方法应该被覆盖,因为定义的集合不会维持插入顺序
答案 7 :(得分: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));
}
这会保留属性文件的顺序
答案 8 :(得分:2)
如果要将Properties导出为XML,则必须覆盖keySet():
public Set<Object> keySet() {
return keys;
}
答案 9 :(得分:2)
有关允许以明确定义的顺序读/写属性文件的完整实现,请参阅https://github.com/etiennestuder/java-ordered-properties。
OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
答案 10 :(得分:2)
我将在这个帖子中再添加一个着名的YAEOOJP(有序Java属性的另一个例子),因为似乎没有人会关心你的默认属性可以喂你的财产。
@see http://docs.oracle.com/javase/tutorial/essential/environment/properties.html
这是我的班级:肯定不符合任何可能的情况,但这对我目前有限的愚蠢目的来说是好的。任何进一步的纠正评论都会受到赞赏,因此大善可以受益。
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;
}
答案 11 :(得分: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);
}
答案 12 :(得分:2)
在一些答案中,假设从文件中读取的属性被放入Properties
的实例(通过调用put
),以便它们出现在文件中。虽然这是一般的行为方式,但我没有看到任何此类订单的保证。
恕我直言:最好逐行读取文件(以保证顺序),而不是使用Properties类作为单个属性的解析器
line,最后将它存储在一些有序的集合中,如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
,之后应该关闭(当然重写它只是一个文件作为参数没有问题。)
答案 13 :(得分:0)
对于那些最近阅读此主题的人: 只需使用org.apache.commons:commons-configuration2中的PropertiesConfiguration类即可。 我已经测试过它可以保持属性排序(因为它在内部使用LinkedHashMap)。 正在执行:
`
PropertiesConfiguration properties = new PropertiesConfiguration();
properties.read(new FileReader("/some/path));
properties.write(new FileWriter("/some/other/path"));
`
仅删除结尾的空格和不必要的转义。
答案 14 :(得分:-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