Properties类按顺序输出加载内容

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类,实现以下功能:

  1. 创建一个能够按照读取顺序来存储properities文件中的key值的集合框架
  2. 能够返回步骤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;
    }
}

四、结果测试

posted @ 2019-08-20 12:27  人之为言  阅读(1277)  评论(0编辑  收藏  举报