3_Java容器

当我们拥有了创建对象的力量后,思索着要怎么把对象存起来,而如何存储取决于要干啥。把跟容器相关的第11章和第17章粗略看了下,记个流水账?

1、容器定义

通常我们需要的是在容器中放置某一类型的对象,Java提供泛型类容器主要是为了可以进行编译期检查。

1.1、基本概念

容器是用来保存对象的,有两种形式:

  • Collection:独立的元素序列,这些元素服从一条或多条规则。
  • Map:一组成对的“键值对”对象,允许使用键来查找值。
1.2、添加元素

Java的工具包中提供了向容器中添加元素的方法。常用Arrays.asList()Collections.addAll()

  • Arrays.asList():接受一个数组或一个用逗号分割的元素列表。
List<Integer> list = Arrays.asList(16, 17,18,19,20);
Integer[] moreInts = {6,7,8,9,10};
list = Arrays.asList(moreInts);

直接使用Arrays.asList()的输出,底层使用数组来表示的,不能修改尺寸。 Arrays.asList()是根据插入的数据的类型推到产生的List的类型,并不在意你对它赋予什么样的类型。

传给Arrays.asList()的参数为new <土豆>, new <萝卜>时,自动推导出来的类型是List<蔬菜>,与引用类型不匹配,编译器报错。想要使用List<食物>,应该进行显式类型参数说明

  • Collections.addAll():接受一个Collection对象、以及一个数组或一个用逗号分割的列表。
Collection<Integer> collection = new ArrayList<Integer>();
Integer[] moreInts = {6,7,8,9,10};
Collections.addAll(collection, 11,12,13,14,15);
Collections.addAll(collection, moreInts);

Collection.addAll()成员方法只能接受另一个Collection对象作为参数,一般是建议先创建一个Collection对象,然后调用Collections.addAll()。

1.3、打印容器

数组:Arrays.toString()来产生数组的可打印表示。
容器:默认的打印行为,可读性就很好啦~

1.4、填充容器

Java工具提供了Collections.nCopies()、Collections.fill()等静态方法来填充容器。but都是复制同一个对象的引用来填充容器的,Collections.fill()只对List对象有用(可以通过把List传递给Collections.addAll()来填充其他类型的容器)。

1.4.1、生成器

基于“所有Collection子类型都有一个接受另一个Collection对象的构造器”这一点。

/**
 * @Description : 适配器模式,使用Generator在容器中放置所需输两的对象,
 * 然后所产生的容器可以传递给任何Collection的构造器。
 * @Author : Ellie
 * @Date : 2018/11/1
 */
public class CollectionData<T> extends ArrayList<T> {
    public CollectionData(Generator<T> gen, int quantity) {
        for (int i = 0; i < quantity; i++) {
            add(gen.next());
        }
    }

    public static <T> CollectionData<T> list(Generator<T> gen, int quantity) {
        return new CollectionData<T>(gen, quantity);
    }
}

这样就可以把CollectionData作为其他Collection类型的构造参数传入来生成啦~其中Generator是用来生成数据的接口。

public interface Generator<T> {
    T next();
}
1.4.2、Map生成器

因为Map有俩需要表示的元素,使用一个Pair类来包一下比较好用啦

public class Pair<K,V> {
    public final K key;
    public final V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

注意域的限定字和访问权限哦(就是没用的事情少做的意思)
要适配模式来生成Map:

public class MapData<K,V> extends LinkedHashMap<K,V> {
    public MapData(Generator<Pair<K,V>> gen, int quantity) {
        for (int i = 0; i < quantity; i++) {
            Pair<K,V> p = gen.next();
            put(p.key, p.value);
        }
    }

    public MapData(Generator<K> genK, Generator<V> genV, int quantity) {
        for (int i = 0; i < quantity; i++) {
            put(genK.next(), genV.next());
        }
    }

    public MapData(Generator<K> genK, V value, int quantity) {
        for (int i = 0; i < quantity; i++) {
            put(genK.next(), value);
        }
    }

    public MapData(Iterable<K> genK, Generator<V> genV) {
        for (K key : genK) {
            put(key, genV.next());
        }
    }

    public MapData(Iterable<K> genK, V value) {
        for (K key : genK) {
            put(key, value);
        }
    }

    public static <K,V> MapData<K,V> map(Generator<Pair<K,V>> gen, int quantity) {
        return new MapData<K,V>(gen, quantity);
    }

    public static <K,V> MapData<K,V> map(Generator<K> genK, Generator<V> genV, int quantity) {
        return new MapData<K,V>(genK, genV, quantity);
    }

    public static <K,V> MapData<K,V> map(Generator<K> genK, V value, int quantity) {
        return new MapData<K,V>(genK, value, quantity);
    }

    public static <K,V> MapData<K,V> map(Iterable<K> genK, Generator<V> genV) {
        return new MapData<K,V>(genK, genV);
    }

    public static <K,V> MapData<K,V> map(Iterable<K> genK, V value) {
        return new MapData<K,V>(genK, value);
    }
}
1.4.3、生成器的一点补充

通过反射机制写一个通用的生成器的示例如下:

public class BasicGenerator<T> implements Generator<T> {
    private Class<T> type;

    public BasicGenerator(Class<T> type) {
        this.type = type;
    }

    @Override
    public T next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }

    public static void main(String[] args){
        Integer a = 1;
        Generator<Coffee> gen = BasicGenerator.create(Coffee.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(gen.next());
        }
    }
}

因为用到了type.newInstance(),所以用BasicGenerator生成对象的类型需要满足:

  • 必须声明为public
  • 必须具备默认的构造器
    包装器类型不满足第二点哦。
1.4.4、定制的Collection和Map的实现 - 享元模式

代码我是看懂了,没看懂享元模式干了啥(归类共享,节省内存)。
留下原话,再看看

FlyWeightMap必须实现entrySet方法,它需要定制Set的实现和定制MapEntry类。这正是享元部分:每个Map.Entry对象都只存储了它的索引,而不是实际的键和值。当调用getKey和getValue时,可以使用索引来得到正确的元素。
与为每个数据都创建MapEntry不同,每个迭代器只有一个MapEntry。Entry对象被用作数据的视窗,只包含在静态字符串数组中的索引,每次调用next方法的时候,索引递增。

//: net/mindview/util/Countries.java
// "Flyweight" Maps and Lists of sample data.
package com.collectionsAndMap;
import java.util.*;

public class Countries {
  public static final String[][] DATA = {
    // Africa
    {"ALGERIA","Algiers"}, {"ANGOLA","Luanda"},
    {"BENIN","Porto-Novo"}, {"BOTSWANA","Gaberone"},
    {"BURKINA FASO","Ouagadougou"},
    {"BURUNDI","Bujumbura"},
    {"CAMEROON","Yaounde"}, {"CAPE VERDE","Praia"},
    {"CENTRAL AFRICAN REPUBLIC","Bangui"},
    {"CHAD","N'djamena"},  {"COMOROS","Moroni"},
    {"CONGO","Brazzaville"}, {"DJIBOUTI","Dijibouti"},
    {"EGYPT","Cairo"}, {"EQUATORIAL GUINEA","Malabo"},
    {"ERITREA","Asmara"}, {"ETHIOPIA","Addis Ababa"},
    {"GABON","Libreville"}, {"THE GAMBIA","Banjul"},
    {"GHANA","Accra"}, {"GUINEA","Conakry"},
    {"BISSAU","Bissau"}
  };
  // Use AbstractMap by implementing entrySet()
  private static class FlyweightMap extends AbstractMap<String,String> {
    private static class Entry implements Map.Entry<String,String> {
      int index;
      Entry(int index) { this.index = index; }
      public boolean equals(Object o) {
        return DATA[index][0].equals(o);
      }
      public String getKey() { return DATA[index][0]; }
      public String getValue() { return DATA[index][1]; }
      public String setValue(String value) {
        throw new UnsupportedOperationException();
      }
      public int hashCode() {
        return DATA[index][0].hashCode();
      }
    }
    // Use AbstractSet by implementing size() & iterator()
    static class EntrySet extends AbstractSet<Map.Entry<String,String>> {
      private int size;
      EntrySet(int size) {
        if(size < 0)
          this.size = 0;
        // Can't be any bigger than the array:
        else if(size > DATA.length)
          this.size = DATA.length;
        else
          this.size = size;
      }
      public int size() { return size; }
      private class Iter implements Iterator<Map.Entry<String,String>> {
        // Only one Entry object per Iterator:
        private Entry entry = new Entry(-1);
        public boolean hasNext() {
          return entry.index < size - 1;
        }
        public Map.Entry<String,String> next() {
          entry.index++;
          return entry;
        }
        public void remove() {
          throw new UnsupportedOperationException();
        }
      }
      public
      Iterator<Map.Entry<String,String>> iterator() {
        return new Iter();
      }
    }
    private static Set<Map.Entry<String,String>> entries = new EntrySet(DATA.length);
    public Set<Map.Entry<String,String>> entrySet() {
      return entries;
    }
  }
  // Create a partial map of 'size' countries:
  static Map<String,String> select(final int size) {
    return new FlyweightMap() {
      public Set<Map.Entry<String,String>> entrySet() {
        return new EntrySet(size);
      }
    };
  }
  static Map<String,String> map = new FlyweightMap();
  public static Map<String,String> capitals() {
    return map; // The entire map
  }
  public static Map<String,String> capitals(int size) {
    return select(size); // A partial map
  }
  static List<String> names =
    new ArrayList<String>(map.keySet());
  // All the names:
  public static List<String> names() { return names; }
  // A partial list:
  public static List<String> names(int size) {
    return new ArrayList<String>(select(size).keySet());
  }
  public static void main(String[] args) {
    System.out.println(new HashMap<String,String>(capitals(3)));
    System.out.println(new LinkedHashMap<String,String>(capitals(3)));
    System.out.println(new TreeMap<String,String>(capitals(3)));
    System.out.println(new Hashtable<String,String>(capitals(3)));
    System.out.println(new HashSet<String>(names(6)));
    System.out.println(new LinkedHashSet<String>(names(6)));
    System.out.println(new TreeSet<String>(names(6)));
    System.out.println(new ArrayList<String>(names(6)));
    System.out.println(new LinkedList<String>(names(6)));
    System.out.println(capitals().get("BRAZIL"));
  }
}
1.5、可选操作

Collection接口中有很多方法是可选操作,主要是为了避免接口爆炸
将方法定义为可选操作应该遵循:

  • UnsupportedOperationException必须是一种罕见事件。
  • 实现接口的时候就要抛出异常。

2、List

可以将元素维护在特定的序列中。
两种类型:

  • 基本ArrayList:适用于随机访问元素,在List的中间插入和移除元素较慢。
  • LinkedList:插入和删除效率较高,随机访问相对较慢。

优化是一个很棘手的问题,最好的策略就是置之不顾,直到发现需要担心它了。

List的常用接口直接Shift+左键啦~

【 LinkedList 】
和ArrayList一样,实现了基本的List接口,插入和移除比ArrayList更高效。随机访问效率较低。

3、Stack

后进先出LIFO的容器,称作栈或叠加栈。LinkedList具有实现栈的所有方法,可以直接将LinkedList作为栈使用。

public class Stack<T> {
    private LinkedList<T> storage = new LinkedList<T>();

    public void push(T v) {
        storage.addFirst(v);
    }

    public T peek() {
        return storage.getFirst();
    }

    public T pop() {
        return storage.removeFirst();
    }

    public boolean empty() {
        return storage.isEmpty();
    }

    @Override
    public String toString() {
        return storage.toString();
    }
}

4、Set

不保存重复的元素,最常用来测试归属性。Set具有与Collection完全一样的接口,没有任何额外的功能,只是行为不同。
HashSet:无序散列,用于快速查找。
TreeSet:有序元素必须实现Comparable接口。可以将比较器作为其构造器参数传入。
LinkedHashSet:具有HashSet查询速度,内部使用链表维护元素顺序(插入次序)。元素必须定义hashCode()方法。

【 SortedSet 】可以保证元素处于排序状态。

  • first():返回容器的第一个元素
  • last():返回容器中的最末一个元素
  • subSet(fromElement, toElement):生成Set的子集[fromElement, toElement)
  • headSet(toElement):生成小于toElement的子集
  • tailSet(fromElement):生成大于等于fromElement的子集。

5、Map

Map与数组和其他的Collection一样,可以很容易地扩展到多维。
Map可以返回键的Set,值的Collection,以及键值对的Set。
映射表的基本思想是它维护的键-值关联。性能是映射表中一个重要问题。HashMap提速是因为使用了散列码来取代键的线性搜索。
关于散列码的相关概念,数据结构专栏读书笔记里写过了,可以参考:散列表和哈希函数
HashMap的性能因子
先了解相关术语:

  • 容量:表中的桶位数
  • 初始容量:表在创建时所拥有的桶位数。
  • 尺寸:表中当前存储的项数
  • 负载因子:尺寸/容量。空表为0,半满表为0.5。HashMap默认的负载因子是0.75。

Map的几种实现:

实现说明
HashMap基于散列表的实现,可以通过构造器设置容量和负载因子来调整容器的性能
LinkedHashMap类似HashMap,迭代遍历时,获取的”键值对“的顺序时其插入次序(LRU)
TreeMap基于红黑树的实现,查看”键“或”键值对“时,它们会被排序。TreeMap是唯一带有subMap方法的Map
WeakHashMap允许释放映射所指向的对象,为解决特殊问题而设计的。如果映射之外没有引用指向某个”键“,则此键可以被垃圾回收
ConcurrentHashMap线程安全的Map,不涉及同步加锁
IdentityHashMap使用==代替equals()对”键“进行比较,为解决特殊问题而设计

想要使用自己的类作为HashMap的键,必须同时重载hashCode()和equals()。
正确的equals方法必须满足下列5个条件:

  • 自反性
  • 对称性
  • 传递性
  • 一致性
  • 对任何不是null的x,x.equals(null)一定返回false。

设计HashCode()最重要额因素就是:无论何时,对同一个对象调用hashCode()都应该生成同样的值。此外,不用改使hashCode()依赖于具有唯一性的对象信息,比如this。
如何写出一份像样的hashCode() :

  • 给int变量result赋予某个非零值常量
  • 为对象内每个有意义的域f(即每个可以做equals()操作的域)计算出一个int散列码c
域类型计算
booleanc = f?0:1
byte、char、short、intc = (int)f
longc = (int)(f^(f>>>32))
floatc = Float.floatToIntBits(f)
doublelong l = Double.doubleToLongBits(f)
Objectc = f.hashCode()
数组对每个元素应用上述规则
  • 合并计算得到散列码 result = result*37 + c
  • 返回result
  • 检查hashCode()最后生成的结果,确保相同的对象有相同的散列码

6、Queue

先进先出的容器。有两个实现LinkedList和PriorityQueue,差异在于排序行为而不是性能。
优先级队列PriorityQueue:下一个弹出的元素是最需要的元素。可以提供自己的comparator来修改默认顺序。
to-do列表示例:

public class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
    static class ToDoItem implements Comparable<ToDoItem> {
        private char primary;
        private int secondary;
        private String item;
        public ToDoItem(String td, char pri, int sec) {
            this.primary = pri;
            this.secondary = sec;
            this.item = td;
        }

        @Override
        public int compareTo(ToDoItem o) {
            if (primary > o.primary) {
                return 1;
            }
            if (primary == o.primary) {
                if (secondary > o.secondary)
                    return 1;
                else
                    return 0;
            }

            return -1;
        }

        @Override
        public String toString() {
            return Character.toString(primary) + secondary + ": " + item;
        }
    }

    public void add(String td, char pri, int sec) {
        super.add(new ToDoItem(td,pri,sec));
    }

    public static void main(String[] args){
        ToDoList toDoList = new ToDoList();
        toDoList.add("Empty trash", 'C', 4);
        toDoList.add("Feed dog", 'A', 2);
        toDoList.add("Feed bird", 'B', 7);
        toDoList.add("Mow lawn", 'C', 3);
        toDoList.add("Water lawn", 'A', 1);
        toDoList.add("Feed cat", 'B', 1);
        while (!toDoList.isEmpty())
            System.out.println(toDoList.remove());
    }
}

7、Collection和Iterator

Collection是描述所有序列容器的共性的根接口
标准C++类库中并没有其容器的任何公共基类,容器之间的共性都是通过迭代器达成的。

7.1、迭代器

迭代器模式:接受对象容器并传递它,从而在每个对象上都执行操作。

轻量级对象,创建代价小。
Iterator只能单向运动,有以下接口:

  • iterator():获取Iterator
  • next():获取序列中的下一个元素
  • hasNext():检查序列中是否还有元素
  • remove():将迭代器新近返回的元素删除,在调用remove之前必须先调用next()。

ListIterator是专用于List类的访问,可以双向移动

  • listIterator(n):指向索引n的ListIterator。
  • hasPrevious()、hasNext()
  • previous()、next()
  • remove()
  • set():设置访问的最后一个元素。

生成Iterator是将队列消费队列的方法连接在一起耦合度最小的方式。

任何实现了Iterable的类都可以用于foreach语句中。
foreach可作用于数组,并不意味着数组也实现了Iterable接口,也不存在数组到Iterable类型的自动转换。

public class ArrayIsNotIterable {
  static <T> void test(Iterable<T> ib) {
    for(T t : ib)
      System.out.print(t + " ");
  }
  public static void main(String[] args) {
    test(Arrays.asList(1, 2, 3));
    String[] strings = { "A", "B", "C" };
    // An array works in foreach, but it's not Iterable:
    //! test(strings);
    // You must explicitly convert it to an Iterable:
    test(Arrays.asList(strings));
  }
} /* Output:
1 2 3 A B C
*///:~

8、如何选择容器接口的不同实现

容器之间的区别通常归结为什么在背后”支持“它们,也就是说,所使用的接口是由什么样的数据接口实现。
书里面给出了用于查看容器之间差异的性能测试,觉得写的很好呢~贴代码

Test:测试用例,抽象类,提供run接口留待实现。
TestParam:测试参数。
Tester:构造容器C、根据测试参数TestParam,执行测试用例Test,给出测试报告(打印测试结果)。

// Test.java
public abstract class Test<C> {
    String name;
    public Test(String name) {
        this.name = name;
    }

    abstract int test(C container, TestParam tp);
}
//TestParam.java
public class TestParam {
    public final int size;
    public final int loops;
    public TestParam(int size, int loops) {
        this.size = size;
        this.loops = loops;
    }

    public static TestParam[] array(int... values) {
        int size = values.length / 2;
        TestParam[] result = new TestParam[size];
        int n = 0;
        for (int i = 0; i < size; i++) {
            result[i] = new TestParam(values[n++], values[n++]);
        }

        return result;
    }

    public static TestParam[] array(String[] values) {
        int vals[] = new int[values.length];
        for (int i = 0; i < vals.length; i++) {
            vals[i] = Integer.decode(values[i]);
        }
        return array(vals);
    }
}
// Tester.java 把格式化打印的部分去掉了没粘
public class Tester<C> {
    protected C container;
    private String headline = "";
    private List<Test<C>> tests;
    public static TestParam[] defaultParams = TestParam.array(
            10,50000,100,50000,1000,50000,10000,5000);
    private TestParam[] paramList = defaultParams;
    public Tester(C container, List<Test<C>> tests) {
        this.container = container;
        this.tests = tests;
        if (container != null) {
            headline = container.getClass().getSimpleName();
        }
    }
    public Tester(C container, List<Test<C>> tests, TestParam[] paramList) {
        this(container, tests);
        this.paramList = paramList;
    }
    public void setHeadline(String newHeadline) {
        headline = newHeadline;
    }
    // 生成测试用的容器
    protected C initialize(int size) {
        return container;
    }
    public static<C> void run(C container, List<Test<C>> tests, TestParam[] paramList) {
        new Tester<C>(container, tests, paramList).timedTest();
    }
    public static <C> void run(C cntnr, List<Test<C>> tests){
        new Tester<C>(cntnr, tests).timedTest();
    }
    public void timedTest() {
        displayHeader();
        for (TestParam param : paramList) {
            System.out.format(sizeField, param.size);
            for (Test<C> test : tests) {
                C container = initialize(param.size);
                long start = System.nanoTime();
                int reps = test.test(container, param);
                long duration = System.nanoTime() - start;
                long timePerRep = duration / reps;
                System.out.format(numberField(), timePerRep);
            }
            System.out.println();
        }
    }
}

使用

public class MapPerformance {
  static List<Test<Map<Integer,Integer>>> tests =
    new ArrayList<Test<Map<Integer,Integer>>>();
  static {
    tests.add(new Test<Map<Integer,Integer>>("put") {
      int test(Map<Integer,Integer> map, TestParam tp) {
        int loops = tp.loops;
        int size = tp.size;
        for(int i = 0; i < loops; i++) {
          map.clear();
          for(int j = 0; j < size; j++)
            map.put(j, j);
        }
        return loops * size;
      }
    });
    tests.add(new Test<Map<Integer,Integer>>("get") {
      int test(Map<Integer,Integer> map, TestParam tp) {
        int loops = tp.loops;
        int span = tp.size * 2;
        for(int i = 0; i < loops; i++)
          for(int j = 0; j < span; j++)
            map.get(j);
        return loops * span;
      }
    });
    tests.add(new Test<Map<Integer,Integer>>("iterate") {
      int test(Map<Integer,Integer> map, TestParam tp) {
        int loops = tp.loops * 10;
        for(int i = 0; i < loops; i ++) {
          Iterator it = map.entrySet().iterator();
          while(it.hasNext())
            it.next();
        }
        return loops * map.size();
      }
    });
  }
  public static void main(String[] args) {
    if(args.length > 0)
      Tester.defaultParams = TestParam.array(args);
    Tester.run(new TreeMap<Integer,Integer>(), tests);
    Tester.run(new HashMap<Integer,Integer>(), tests);
    Tester.run(new LinkedHashMap<Integer,Integer>(),tests);
    Tester.run(new IdentityHashMap<Integer,Integer>(), tests);
    Tester.run(new WeakHashMap<Integer,Integer>(), tests);
    Tester.run(new Hashtable<Integer,Integer>(), tests);
  }
}

9、其他

1、容器有很多好用的静态方法(我也没用过),意思就是可劲儿想办法偷懒,Java都会给你一把斧子。
2、设定不可修改

Collections.unmodifiableList(new ArrayList<String>(data));
Collections.unmodifiableMap(new HashMap<String,String>(data));

3、同步控制:Collections类有办法自动同步容器

Collections.synchronizedList(new ArrayList<String>(data));
Collections.synchronizedMap(new HashMap<String,String>(data));

4、快速报错,多个进程同时修改同一个容器,Java会启动快速报错,抛出ConcurrentModficationException异常。

10、持有引用

对象可被回收的状态是”不可获得“,”可获得“指的是对象可在程序中被找到。如果想要持有某个对象的引用,但同时允许内存不够的时候垃圾回收器释放它,就应该使用Reference对象。
用于提高垃圾回收的灵活性,设计了继承自抽象类Reference的三大子类,对应不同级别的可获得性:

  • SoftReference:用以实现内存敏感的高速缓存,在最后一次引用后保持的事件是根据堆剩余空间计算的。
  • WeakReference:为实现规范映射。”规范映射“中对象的实例可以在程序的多处被同时使用,以节省空间。
  • PhantomReference:用于调度回收钱的清理工作,比终止机制更加灵活。
    使用SoftReference和WeakReference可以选择是否放入RefernceQueue,PhantomReference则只能依赖于RefernceQueue,因为get方法只能获取到null。
    【引用队列的使用】
Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
counter = null;
System.gc();
try {
    // Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
    Reference<Object> ref = refQueue.remove(1000L);
    if (ref != null) {
        // do something
    }
} catch (InterruptedException e) {
    // Handle it
}

Java的可达级别:

  • 强可达:当一个对象可以有一个或多个线程可以不通过各种引用访问到的情况。如创建对象的线程对该对象就是强可达。
  • 软可达:只能通过软引用才能访问到的对象的状态。
  • 弱可达:只能通过弱引用访问。此时十分临近finalize状态。
  • 幻象可达:finalize了。
  • 不可达:可回收了。

Reachability Fence机制
当一个对象本身没有强引用,但是部分属性还在被使用,此时就需要进行保护。

class Resource {
 private static ExternalResource[] externalResourceArray = ...
 int myIndex; Resource(...) {
     myIndex = ...
     externalResourceArray[myIndex] = ...;
     ...
 }
 protected void finalize() {
     externalResourceArray[myIndex] = null;
     ...
 }
 public void action() {
 try {
     // 需要被保护的代码
     int i = myIndex;
     Resource.update(externalResourceArray[i]);
 } finally {
     // 调用 reachbilityFence,明确保障对象 strongly reachable
     Reference.reachabilityFence(this);
 }
 }
 private static void update(ExternalResource ext) {
    ext.status = ...;
 }
} 

附上杨晓峰在《Java核心计数36讲》里面的一个问题和答案。 https://time.geekbang.org/column/article/6970

11、总结

  • 数组将数字与对象联系起来,保存类型明确的对象,查询对象时,不需要对结果做类型转换。可以是多维的,可以保存基本类型的数据。缺点是,一旦生成,其容量不能改变。
  • Collection保存单一的元素,Map保存相关联的键值对。容器不能持有基本类型,但是自动包装机制会仔细执行基本类型到容器中所持有的包装器类型之间的双向转换。
  • List建立数字索引与对象的关联,但是List能够自动扩充容量。
  • 大量随机访问时,推荐使用ArrayList。经常插入或删除,使用LinkedList。
  • 各种Queue及栈的行为,由LinkedList提供支持。
  • Map时一种将对象(不是数字)与对象相关联的设计。HashMap用来快速访问。TreeMap保持“键”始终处于排序状态,没有HashMap快。LinkedHashMap保持元素插入的顺序,同事也通过散列提供了快速访问能力。
  • Set不接受重复元素。HashSet提供最快的查询速度,TreeSet保持元素处于排序状态,LinkedHashSet以插入顺序保存元素。
  • 不推荐使用过时的Vector、HashTable和Stack。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值