2014年8月

Java中Map这种数据结构非常常用,也非常实用。最常用的方法就是下面几个:

V get(Object key);
V put(K key, V value);
V remove(Object key);
boolean containsKey(Object key);

不过,对于Map的遍历操作还是很常用的。下面就介绍几种Map的遍历操作方法。

通过key的遍历

首先拿到Map的key的集合Set,然后使用Set的迭代器Iterator,遍历每个key,通过Map的get()方法获取其值。

Set<Object> set = map.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上述代码的前两行可以合成一行:

Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上面这个版本就是我原来最常用的遍历方式。这个版本的进化版本就是使用foreach:

for (Object key : map.keySet()) {
     System.err.println(key + " " + map.get(key));
}

这类方法的主要思想就是拿到key,然后使用get()方法拿到值。

通过Entry

Entry是Map的内部接口:

interface Entry<K,V>{
    K getKey();
    V getValue();
    V setValue(V value);
    boolean equals(Object o);
    int hashCode();
}

可用通过遍历Map的Entry,调用Entry的getKey()方法和getValue()方法拿到键和值。

Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry entry = (Map.Entry) iterator.next();
    System.err.println(entry.getKey() + " " + entry.getValue());
}

更加简单的foreach版本如下:

for (Map.Entry<Object, Object> entry : map.entrySet()) {
    System.err.println(entry.getKey() + " " + entry.getValue());
}

这类方法的主要思想就是拿到Map的Entry,Entry就是键值对的组合。从效率上来说,通过Entry效率要稍高。http://stackoverflow.com/questions/3870064/performance-considerations-for-keyset-and-entryset-of-map

参考JavaCodeGeeks上个这篇文章 http://www.javacodegeeks.com/2011/12/cloning-of-serializable-and-non.html 作者Craig Flichel

通常开发者都会依赖于第三方的类库来进行开发,这样避免重复制造轮子。特别是在Java的世界中,有着像Apache和Spring这样流行的开源项目。但在实际使用过程,我们总是无法控制这些类的行为,或者是只能控制一点点,叫人不爽。

这有时会带来问题。举个例子,比如说你要深度克隆一个没有提供合适克隆方法的对象,你准备咋整?

通过序列化来克隆

最简单的方法,如果对象实现了Serializable接口,那么直接利用这个就好。Apache Commons库中提供了一个方法去做这个,但为了完整,自己实现的话,代码可能就像下面这样:

public static Serializable cloneThroughSerialize(Serializable ser) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    serializeToOutputStream(ser, bos);
    byte[] bytes = bos.toByteArray();
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
            bytes));
    return (Serializable) ois.readObject();
}

private static void serializeToOutputStream(Serializable ser,
        OutputStream os) throws IOException {
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(os);
        oos.writeObject(ser);
        oos.flush();
    } finally {
        oos.close();
    }
}

// using our custom method
Serializable cloned = cloneThroughSerialize(someObject);

// or with Apache Commons
Object cloned = org.apache.commons.lang.SerializationUtils.clone(someObject);

- 阅读剩余部分 -

参考JavaCodeGeeks上个这篇文章 http://www.javacodegeeks.com/2014/02/serializing-java-objects-with-non-serializable-attributes.html 作者Dustin Marx

有很多原因,使得我们想使用自定义的序列化方法代替Java默认的序列化方法。最普遍的一个原因就是为了提升性能。其他的原因大都是默认的序列化机制不支持。其中一个非常普遍的场景就是,要序列化一个拥有非序列化属性的对象。

下面的代码展示了一个小例子:

SerializationDemonstrator.java
package com.chengjf;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializationDemonstrator {
    public static <T> void serialize(final T objectToSerialize,
            final String fileName) {
        if (fileName == null) {
            throw new IllegalArgumentException(
                    "Name of file to which to serialize object to cannot be null.");
        }
        if (objectToSerialize == null) {
            throw new IllegalArgumentException(
                    "Object to be serialized cannot be null.");
        }
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            fos = new FileOutputStream(fileName);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(objectToSerialize);
            System.out.println("Serialization of object " + objectToSerialize
                    + " completed!");
            oos.close();
            fos.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    public static <T> T deserialize(final String fileToSerialize,
            final Class<T> classBeingDeserialized) {
        if (fileToSerialize == null) {
            throw new IllegalArgumentException(
                    "Cannot deserialize from a null filename.");
        }
        if (classBeingDeserialized == null) {
            throw new IllegalArgumentException(
                    "Type of class to be deserialized cannot be null.");
        }
        T objectOut = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(fileToSerialize);
            ois = new ObjectInputStream(fis);
            objectOut = (T) ois.readObject();
            System.out.println("Deserialization of object " + objectOut
                    + " is completed!");
            ois.close();
            fis.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException classNotFoundException) {
            classNotFoundException.printStackTrace();
        }
        return objectOut;
    }
}

- 阅读剩余部分 -

Preferences API与对象序列化相比,前者与对象持久性更密切,因为它可以自动存储和读取信息。不过,它只能用于小的、受限的数据集合——我们只能存储基本类型和字符串,并且每个字符串的存储长度不能超过8K(不是很小,但我们也并不想用它来创建任何重要的东西)。顾名思义,Preferences API用于存储和读取用户的偏好(preferences)以及程序配置项的设置。

Preferences是一个键-值集合(类似映射),存储在一个节点层次结构中。尽管节点层次结构可用来创建更为复杂的结构,但通常是创建以你的类名命名的单一节点,然后将信息存储于其中。

    // : io/PreferencesDemo.java
    import java.util.prefs.*;

    public class PreferencesDemo {
    public static void main(String[] args) throws Exception {
        Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);
        prefs.put("Location", "Xi'an");
        prefs.put("Name", "chengjf");
        prefs.putInt("Age", 24);
        prefs.putBoolean("Is he a coder?");
        int usageCount = prefs.getInt("UsageCount", 0);
        usageCoun++;
        prefs.putInt("UsageCount", usageCount);
        for(String key : prefs.getKeys()){
            System.out.println(key + " : " + prefs.get(key, null));
        }
        // You must always provide a default value:
        System.out.println("How old is he? " + prefs.getInt("Age", 0);
    }
    }

    // Output: (Sample)
    ......
    //

最好将“user”用于个别用户的偏好,将“system”用于通用的安装配置。
尽管我们不一定非要把当前的类作为节点标示符,但这仍不失为一种很有用的方法。

一旦我们创建了节点,就可以用它来加载或者读取数据了。在这个例子中,向节点载入了各种不同类型的数据项,然后获取其keys()。它们是以String[]的形式返回的,如果你习惯于keys()属于集合类库,那么这个返回结果可能并不是你所期望的。注意get()的第二个参数,如果某个关键字下没有任何条目,那么这个参数就是所产生的默认值。当在一个关键字集合内迭代时,我们总要确信条目是存在的,因此用null作为默认值是安全的,但是通常我们会获得一个具名的关键字,就像下面这条语句:

    prefs.getInt("Companions", 0);

在通常情况下,我们希望提供一个合理的默认值。实际上,典型的习惯用法可见于下面几行:

    int usageCount = prefs.getInt("UsageCount", 0);
    usageCount++;
    prefs.putInt("UsageCount", usageCount);

这样,在我们第一次运行程序时,UsageCount的值是0,但在随后引用中,它将会是非零值。

在我们运行PreferencesDemo.java时,会发现每次运行程序时,UsageCount的值都会增加1,但是数据存储到哪里了呢?在程序第一次运行之后,并没有出现任何本地文件。Preferences API利用合适的系统资源完成了这个任务,并且这些资源会随着操作系统不同而不同。例如在Windows里,就是用注册表(因为它已经有“键值对”这样的节点对层次结构了)。但是最重要的一点是,它已经神奇般地为我们存储了信息,所以我们不必担心不同的操作系统是怎么运作的。

本文Ctrl-V自《Java编程思想》第四版第X章Y小节。

序列化简介

Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可以通过网络进行,这意味着序列化机制能自动弥补不同操作系统之间的差异。也就是说,可以在运行Windows系统的计算机上创建一个对象,将其序列化,通过网络将它发送给一台运行Unix系统的计算机,然后在那里准确地重新组装,而却不必担心数据在不同机器上的表示会不同,也不必关心字节的顺序或者其他任何细节。

就其本身来说,对象的序列化是非常有趣的,因为利用它可以实现轻量级持久性(lightweight persistence)。“持久性”意味着一个对象的生命周期并不取决于程序是否正在执行;它可以生存于程序的调用之间。通过将一个序列化对象写入磁盘,然后在重新调用程序时恢复该对象,就能够实现持久性的效果。之所以称其为“轻量级”,是因为不能用某种“persistent”(持久)关键字来简单地定义一个对象,并让系统自动维护其他细节问题(尽管将来有可能实现)。相反,对象必须在程序中显式地序列化(serialize)和反序列化还原(deserialize)。如果需要一个更严格的持久性机制,可以考虑像Hibernate之类的工具。

- 阅读剩余部分 -