234390216 阅读(438) 评论(0)

实现对properties文件的有序读写

 

         最近遇到一项需求,要求把properties文件中的内容读取出来供用户修改,修改完后需要再重新保存到properties文件中。很简单的需求吧,可问题是Properties是继承自HashTable的,直接通过keySet()keys()entrySet()方法对Properties中的元素进行遍历时取出来的内容顺序与properties文件中的顺序不一致,这是问题一;问题二是就算取出来的时候是有序的,保存到文件中时又是无序的了。

         当然,解决这两个问题的方法有很多。我最终采用的方法是自定义一个PropertiesUtil类,该类继承自PropertiesPropertiesUtil提供一个返回由key按照存入顺序组成的List的方法,getKeyList(),这样问题一就解决了。那如何保证getKeyList()方法返回的就是有序的key组成的集合呢?我查看了一下Properties方法的源码,发现其setProperty()方法实际上就是调用了父类HashTableput()方法,其次Properties在从文件中加载内容时是按照文件顺序进行读取,然后调用父类HashTableput()方法进行储存。所以问题的解决办法就是PropertiesUtil持有一个私有的可以有序存储key的集合,然后重写父类的put()方法,在方法体中照常通过super.put()进行属性的存储,同时将key添加到存储key的集合中。

         Properties提供有save()方法和store()方法可以将当前对象的内容存放到指定的输出流中,但它们的底层逻辑都是一样的。通过调用keys()方法获取一个Enumeration,然后对该Enumeration进行遍历,依次将对应的keyvalue写入到输出流中,所以要保证写入是有序的,就要保证遍历keys()返回的Enumeration时取出的元素key是有序的。所以解决方法是重写keys()方法,保证遍历返回的Enumeration时得到的key是有序的。完整代码如下:

 

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
 
public class PropertiesUtil extends Properties {
 
   private static final long serialVersionUID = 1L;
 
   private ListEnumerationAdapter<Object> keyList = new ListEnumerationAdapter<Object>();
  
   /**
    * 默认构造方法
    */
   public PropertiesUtil() {
     
   }
  
   /**
    * 从指定路径加载信息到Properties
    * @param path
    */
   public PropertiesUtil(String path) {
      try {
         InputStream is = new FileInputStream(path);
         this.load(is);
      } catch (FileNotFoundException e) {
         e.printStackTrace();
         thrownew RuntimeException("指定文件不存在!");
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
  
   /**
    * 重写put方法,按照property的存入顺序保存key到keyList,遇到重复的后者将覆盖前者。
    */
   @Override
   public synchronized Object put(Object key, Object value) {
      if (keyList.contains(key)) {
         keyList.remove(key);
      }
      keyList.add(key);
      returnsuper.put(key, value);
   }
  
   /**
    * 获取Properties中key的有序集合
    * @return
    */
   public List<Object> getKeyList() {
      returnkeyList;
   }
  
   /**
    * 保存Properties到指定文件,默认使用UTF-8编码
    * @param path 指定文件路径
    */
   public void store(String path) {
      this.store(path, "UTF-8");
   }
  
   /**
    * 保存Properties到指定文件,并指定对应存放编码
    * @param path 指定路径
    * @param charset 文件编码
    */
   public void store(String path, String charset) {
      if (path != null && !"".equals(path)) {
         try {
            OutputStream os = new FileOutputStream(path);
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, charset));
            this.store(bw, null);
            bw.close();
         } catch (FileNotFoundException e) {
            e.printStackTrace();
         } catch (IOException e) {
            e.printStackTrace();
         }
      } else {
         thrownew RuntimeException("存储路径不能为空!");
      }
   }
 
   /**
    * 重写keys方法
    */
   @Override
   public synchronized Enumeration<Object> keys() {
      keyList.reset();
      returnkeyList;
   }
   
   /**
    * ArrayList到Enumeration的适配器
    */
    private static class ListEnumerationAdapter<T> extends ArrayList<T> implements Enumeration<T> {
      private static final long serialVersionUID = 1L;
      private int index = 0;
     
      public boolean hasMoreElements() {
         returnindex < this.size();
      }
 
      public T nextElement() {
         if (this.hasMoreElements()) {
            returnthis.get(index++);
         }
         returnnull;
      }
     
      /**
       * 重置index的值为0,使得Enumeration可以继续从头开始遍历
       */
      public void reset() {
         this.index = 0;
      }
    }
}