Properties類按順序輸出加載內容
最近手寫工廠的時候,遇到了加載配置文件時不按照properties文件中的數據的順序來加載。
一、問題代碼
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) {
InputStream ips = null;
try {
ips = Properties.class.getResourceAsStream("/test.properities");
Properties props = new Properties();
props.load(ips);
for(String name:props.stringPropertyNames())
System.out.println(props.getProperty(name) + " "+name);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(ips != null){
ips.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
配置文件
cat=1
dog=2
bird=3
mouse=4
pig=5
輸出結果
二、原因分析
public class Properties extends Hashtable<Object,Object>
上面是Properties類的定義,可以看到它繼承了Hashtable類
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
load方法調用load0方法
private void load0 (LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
int limit;
int keyLen;
int valueStart;
char c;
boolean hasSep;
boolean precedingBackslash;
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
//System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
precedingBackslash = false;
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
//need check if escaped.
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
}
else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
put(key, value);
}
}
load0方法可以看到最后取到key和value值后會調用父類Hashtable的put()
方法,把數據存入Hashtable,具體的Hashtable源碼這里就不再貼出。簡單來說Hashtable的put()
方法接收到值后會按照哈希值存儲,而不是按照讀取順序存儲。
接下來是讀取時的源碼,有興趣的可以看一下:
public Set<String> stringPropertyNames() {
Hashtable<String, String> h = new Hashtable<>();
enumerateStringProperties(h);
return h.keySet();
}
enumerateStringProperties
方法:
private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
if (defaults != null) {
defaults.enumerateStringProperties(h);
}
for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
Object k = e.nextElement();
Object v = get(k);
if (k instanceof String && v instanceof String) {
h.put((String) k, (String) v);
}
}
}
Hashtable的keySet()
方法
public Set<K> keySet() {
if (keySet == null)
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}
三、解決方法
寫一個工具類繼承Properties類,實現以下功能:
- 創建一個能夠按照讀取順序來存儲properities文件中的key值的集合框架
- 能夠返回步驟1創建的集合
在這里我采用了LinkedList
來順序存儲key值(也可以采用別的集合類型),然后重寫put方法,先把值存入自己建的LinkedList
中,再調用父類的方法。關於返回Key值的集合是新寫了一個orderStringPropertyNames()
方法返回LinkedList
實現代碼如下:
import java.util.LinkedList;
import java.util.Properties;
public class LinkedProperities extends Properties {
private LinkedList<String> linkedList = new LinkedList<String>();
@Override
public synchronized Object put(Object key, Object value) {
linkedList.add((String) key);
return super.put(key, value);
}
public LinkedList<String> orderStringPropertyNames() {
return linkedList;
}
}