java_集合体系之Map体系总结(二)

3.3、 LinkedHashMap

3.31 LinkedHashMap特点:

Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此链接列表定义了迭代顺序(即存储的顺序与输出的顺序相同),该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。此实现不是同步的

注意

①当key为String或基本数据类型包装类,键相同自动替换旧值为新值 (因为他们已重写了hashCode与equals方法)
②当key为自定义对象,需让其重写hashCode与equals方法才能保证key的唯一性

3.32 LinkedHashMap使用示例

public class LinkedHashMapReview {
    public static void main(String[] args) {
        test1();
        test2();
        test3();
    }

    /**
     * 自定义对象为key
     * 需让其重写hashCode与equals方法才能保证key的唯一性
     */
    private static void test3() {
        LinkedHashMap<Info, Integer> map=new LinkedHashMap<Info, Integer>();
        map.put(new Info(0, "vvv"), 000);
        map.put(new Info(0, "vvv"), 333);
        map.put(new Info(1, "ccc"), 111);
        System.out.println(map);
        // output :  当Info没有重写hashCode与equals方法,
//      {Info [id=0, adress=vvv]=0, Info [id=0, adress=vvv]=333, Info [id=1, adress=ccc]=111}

        // output :  当Info重写hashCode与equals方法
//      {Info [id=0, adress=vvv]=333, Info [id=1, adress=ccc]=111}

    }

    /**
     * 基本数据类型包装类重写了hashCode与equals方法,键相同自动替换旧值为新值
     */
    private static void test2() {
        LinkedHashMap<Integer, Integer> map=new LinkedHashMap<Integer, Integer>();
        map.put(1, 66);
        map.put(2, 67);
        map.put(3, 68);
        map.put(1, 69);  // 自动装箱 Integer.valueOf(69)
        System.out.println(map);
        // output
//      {1=69, 2=67, 3=68}
        // 输出顺序与存储相同,重复添加已有的键会替换掉旧值
    }

    /**
     * String做key
     * String类实现了hashCode与equals方法,键相同自动替换旧值为新值
     */
    private static void test1() {
        LinkedHashMap<String, String> map=new LinkedHashMap<String, String>();
        map.put("aa", "121");
        map.put("bb", "122");
        map.put("cc", "123");
        map.put("aa", "122");
        map.put("dd", "122");
        map.put("ee", "122");

        System.out.println(map);
        // output 
        // {aa=122, bb=122, cc=123, dd=122, ee=122}
        // 输出顺序与存储相同,重复添加已有的键会替换掉旧值
    }
}

3.4、 Hashtable

Hashtable是同步的,它不允许使用 null 值和 null 键。除此之外与HashMap大致相同,示例略 

3.41 使用Properties

Properties类继承自Hashtable,表示了一个持久的属性集,由键值对(key-value)组成。

与Hashtable不同的是,Properties 属性列表中每个键及其对应值都是一个字符串,因此不推荐使用Hashtable的put方法为Properties添加属性。

Properties 可通过store方法保存在流中或通过load方法从流中加载。如下三种方式:

1.通过字节输入输出流

 void load(InputStream inStream) 
                  从输入流中读取属性列表(键和元素对)。 
 void store(OutputStream out, String comments) 
                  以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。 

2.通过字符输入输出流

 void load(Reader reader) 
              按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 
void store(Writer writer, String comments) 
              以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。 

3.通过xml

void loadFromXML(InputStream in) 
              将指定输入流中由 XML 文档所表示的所有属性加载到此属性表中。   
void storeToXML(OutputStream os, String comment) 
              发出一个表示此表中包含的所有属性的 XML 文档。 
void storeToXML(OutputStream os, String comment, String encoding) 
              使用指定的编码发出一个表示此表中包含的所有属性的 XML 文档。  

通过如下方法存取键值

 String getProperty(String key) 
              用指定的键在此属性列表中搜索属性。 
String getProperty(String key, String defaultValue) 
              用指定的键在属性列表中搜索属性。 
 Object setProperty(String key, String value) 
              调用 Hashtable 的方法 put。 

 void list(PrintStream out) 
              将属性列表输出到指定的输出流。 
 void list(PrintWriter out) 
              将属性列表输出到指定的输出流。 
 Set<String> stringPropertyNames() 
              返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。 

下面通过示例使用Properties
1.创建一个属性文件

/**
     * 创建一个属性文件
     * @throws IOException
     */
    private static void create() throws IOException {
        // 创建一个空的属性文件
        Properties prop = new Properties();
        // 创建一个文件输出流,用于写出属性文件到本地
        FileWriter writer = new FileWriter("config.properties");//亦可以使用FileOutputStream
        // 设置属性键与值
        prop.setProperty("name", "pecuyu");
        prop.setProperty("character", "kind");
        // 通过字符输出流将键值对写入属性文件
        prop.store(writer, "this is new");
        writer.close();
    }

2.读取属性文件内容

    /**
     * 读取一个属性文件
     */
    private static void load() throws FileNotFoundException, IOException {
        // 创建属性文件
        Properties prop=new Properties();
        // 从流中读取属性列表到属性文件
        prop.load(new FileInputStream("config.properties"));// 亦可使用FileReader
        // 通过键取值
        String name = prop.getProperty("name");
        String character = prop.getProperty("character");
        //System.out.println("name="+name+" character="+character);

        prop.list(System.out); // 将属性键值对列出并打印到控制台

        Set<String> stringPropertyNames = prop.stringPropertyNames();// 键的set集合
        for (String key : stringPropertyNames) {
            String value = prop.getProperty(key);  // 通过键获取值
            System.out.println("key="+key+" value="+value);
        }

    }

3.5、 WeakHashMap

3.51 特点

以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。 
null 值和 null 键都被支持

3.52 实现注意事项

WeakHashMap 中的值对象由普通的强引用保持。因此应该小心谨慎,确保值对象不会直接或间接地强引用其自身的键,因为这会阻止键的丢弃。注意,值对象可以通过 WeakHashMap 本身间接引用其对应的键;这就是说,某个值对象可能强引用某个其他的键对象,而与该键对象相关联的值对象转而强引用第一个值对象的键。处理此问题的一种方法是,在插入前将值自身包装在 WeakReferences 中,如:m.put(key, new WeakReference(value)),然后,分别用 get 进行解包。 

3.6、 TreeMap

3.61 TreeMap特点

- 基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
- 键值对是红黑树结构,可以保证键的排序和唯一性
- 此实现不是同步的

须为TreeMap提供排序方案

①根据其键的自然顺序进行排序(自定义对象须实现Comparable接口并重写compare方法)
②根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。

3.62 分析TreeMap的put方法源码

1、判断Entry是否有元素,没有则new一个,并添加新元素
2、通过自然排序与比较器排序来为TreeMap排序,优先使用Comparator来排序

通过Entry对象来来确保插入元素的唯一性,它建立在compare方法的基础上,此方法返回0时,表示插入的键存在,直接替换旧值并返回。因此在使用TreeMap时,自定义对象须实现Comparable接口并重写compare方法或在创建TreeMap时提供Comparator
    cmp = cpr.compare(key, t.key);
     if (cmp < 0)
          t = t.left;
      else if (cmp > 0)
          t = t.right;
      else
      return t.setValue(value);

3、在插入元素后重新调整红黑树(即Entry树)

源码如下

public V put(K key, V value) {
        Entry<K,V> t = root;
        // 判断Entry是否有元素,没有则new一个
        if (t == null) {
         compare(key, key); // type (and possibly null) check
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null; 
        }
        int cmp;
        Entry<K,V> parent;
        // 通过自然排序与比较器排序
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

3.63 TreeMap使用示例

public class TreeMapReview {
    public static void main(String[] args) {
        test1();
        test2();
    }

    /**
     * 自定义对象做key,需满足以下两个条件之一,否则抛出异常java.lang.ClassCastException:com.yu.bean.Info cannot be cast to java.lang.Comparable
     * ①实现Comparable接口并重写compare方法
     * ②构造TreeMap对象时,需传入Comparator
     * 当两者都有,以Comparator来排序
     */
    private static void test2() {
        TreeMap<Info, String> map=new TreeMap<Info, String>(new Comparator<Info>() {
            @Override
            public int compare(Info o1, Info o2) {
                int num = o2.getId() - o1.getId();
                num = num == 0 ? o2.getAdress().hashCode() - o1.getAdress().hashCode() : num;
                return num;
            }
        });
        map.put(new Info(000, "hhh"), "qqq");
        map.put(new Info(001, "hhh"), "aaa");
        map.put(new Info(002, "hhh"), "zzz");
        map.put(new Info(000, "hhh"), "qqq");

        System.out.println(map);
    }

    /**
     * String类型或基本类型包装类做key
     * String类实现了Comparable接口,可以进行自然排序
     */
    private static void test1() {
        TreeMap<String, String> map = new TreeMap<String, String>();
        map.put("a", "111");
        map.put("b", "123");
        map.put("c", "121");
        map.put("c", "121");

        Set<Entry<String, String>> entrySet = map.entrySet();
        for (Entry<String, String> entry : entrySet) {
            System.out.println("key=" + entry.getKey() + " value="
                    + entry.getValue());
        }
        // output:
//      key=a value=111
//      key=b value=123
//      key=c value=121
    }
}

猜你喜欢

转载自1049097489.iteye.com/blog/2392519