第17章 容器深入研究

第17章 容器深入研究

完整的容器分类法

容器可以分为四大类:List/SetMap/Queue,常用的具体实现是:ArrayList/HashSet/HashMap。

17.2 填充容器

public static void main(String[] args) {
        List<StringAddress> list = new ArrayList<>();
        // 只能替换已有的元素,不能新增元素
        Collections.fill(list, new StringAddress("b"));
        System.out.println(list);
    	// []
        // 新增一个List,元素都指向同一个位置
        list = Collections.nCopies(3, new StringAddress("a"));
        System.out.println(list);
    	// [com.container.StringAddress@27c170f0 a, com.container.StringAddress@27c170f0 a, com.container.StringAddress@27c170f0 a]
        Collections.fill(list, new StringAddress("b"));
        System.out.println(list);
    // [com.container.StringAddress@5451c3a8 b, com.container.StringAddress@5451c3a8 b, com.container.StringAddress@5451c3a8 b]
    }

一种Generator解决方案

class CollectionData<T> extends ArrayList<T>{
    // 填充进CollectionData
    public CollectionData(Generator<T> generator,int num) {
        for (int i = 0; i < num; i++) {
            add(generator.next());
        }
    }
    
    // 注意:类泛型不能用于静态方法
    public static <E>CollectionData<E> list(Generator<E> generator,int num){
        return new CollectionData<>(generator,num);
    }
}

class Proverb implements Generator<String>{

    // 日常的单调很容易使人精疲力尽
    String[] strings = "It’s easy to get burned out by the daily grind".split(" ");
    int next = 0;
    @Override
    public String next() {
        return strings[next++];
    }
}

 public static void main(String[] args) {
        LinkedHashSet/*维持插入的顺序*/<String> set = new LinkedHashSet<>(new CollectionData<>(new Proverb(),5));
        System.out.println(set); // [It’s, easy, to, get, burned]
        set.addAll(CollectionData.list(new Proverb(),5));
        System.out.println(set); // [It’s, easy, to, get, burned]
    }

Map生成器


class MapData<K,V> extends LinkedHashMap<K,V>{
    // 一对值
    public MapData(Generator<Pair<K,V>> generator,int num){
        for (int i = 0; i < num; i++) {
            Pair<K, V> next = generator.next();
            put(next.k,next.v);
        }
    }

    // key和value
    public MapData(Generator<K> kGenerator,Generator<V> vGenerator,int num){
        for (int i = 0; i < num; i++) {
            put(kGenerator.next(),vGenerator.next());
        }
    }

    // 系列key,单一value
    public MapData(Iterable<K> iterable,V value){
     iterable.forEach(key -> put(key,value));

    }
}

class Pair<K,V>{
    public final K k;
    public final V v;

    public Pair(K k, V v) {
        this.k = k;
        this.v = v;
    }
}


class Letters implements Generator<Pair<Integer,String>>,Iterable<Integer>{
    private int number = 65;
    private char aChar = 'A';
    @Override
    public Pair<Integer, String> next() {
        return new Pair<>(number++,String.valueOf(aChar++));
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return number < 70;
            }

            @Override
            public Integer next() {
                return number++;
            }
        };
    }
}

 public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>(new MapData<>(new Letters(),5));
        System.out.println(map);
     // {65=A, 66=B, 67=C, 68=D, 69=E}
        map.putAll(new MapData<>(new RandomGenerator.Int(),new RandomGenerator.Str(),5));
        System.out.println(map);
     // {65=A, 66=B, 67=C, 68=D, 69=E, -151806374=bc, 1239779180=EF, -735006902=cdE, -95006371=cdEF, 118002227=cdE}
        map.putAll(new MapData<>(new Letters(),"单一"));
        System.out.println(map);
     // {65=单一, 66=单一, 67=单一, 68=单一, 69=单一, -151806374=bc, 1239779180=EF, -735006902=cdE, -95006371=cdEF, 118002227=cdE}
    }

使用Abstract类

实现容器对应的Abstract容器,并仅产生只读的容器,可以使实现的方法最少。

在该实例中同时使用了享元模式,容器中共享元素。享元模式使对象的一部分具体化,减少了占用空间。

注意:Countries类中的关系

Countries内部类:MyMap
MyMap内部类:Entity	EntrySet
EntrySet内部类:Iter
capitals()方法:产生包含国家和首都的Map容器
names()方法:产生包含国家的List容器
class Countries {
    public static final String[][] DATA = {
            {"中国", "北京"}, {"日本", "东京"},
            {"泰国", "曼谷"}, {"印度", "新德里"}
    };

    // 实现抽象Map
    private static class MyMap extends AbstractMap<String, String> {

        // 单个实体类
        // 仅包含对静态数组的引用,而不包含具体的数据
         static class Entity implements Map.Entry<String, String> {
            // 通过index控制对DATA的数据访问
             int index;

            public Entity(int index) {
                this.index = index;
            }

            @Override
            public String getKey() {
                // 实现共享DATA中的数据
                return DATA[index][0];
            }

            @Override
            public String getValue() {
                return DATA[index][1];
            }

            @Override
            public String setValue(String value) {
                throw new RuntimeException("不允许存入");
            }
        }

        // 实现Set类
         static class EntrySet extends AbstractSet<Map.Entry<String,String>> {
            // 控制Map的大小
             int size;

            public EntrySet(int size) {
                // 确定Set的大小
                if (size < 0)
                    this.size = 0;
                else if (size > DATA.length)
                    this.size = DATA.length;
                else
                    this.size = size;
            }

            // 实现自己的迭代器
            private  class Iter implements Iterator<Map.Entry<String, String>>{
                // 每个迭代器仅包含一个Entity实体类,其起到视窗的作用,迭代器通过对index的操作,使其指向下一个元素
                private Entity entity = new Entity(-1);
                @Override
                public boolean hasNext() {
                    return entity.index<size-1;
                }

                @Override
                public Entry<String, String> next() {
                    // 移动指针
                    entity.index++;
                    return entity;
                }
            }
            @Override
            public Iterator<Map.Entry<String,String>> iterator() {
                return new Iter();
            }

            @Override
            public int size() {
                return size;
            }
        }
        // 创建静态Set
        private static Set<Map.Entry<String, String>> entrySet = new EntrySet(DATA.length);

        @Override
        public Set<Entry<String, String>> entrySet() {
            return entrySet;
        }
    }

    // 返回指定数量的Map
    static Map<String,String> select(final int size){
        return new MyMap(){
            @Override
            public Set<Entry<String, String>> entrySet() {
                return new EntrySet(size);
            }
        };
    }

    // 所有国家和首都
    static Map<String, String> map = new MyMap();

    public static Map<String, String> capitals(){
        return map;
    }
    public static Map<String, String> capitals(int size){
        return select(size);
    }

    // 所有国家名字
     static List<String> list = new ArrayList<>(map.keySet());

    public static List<String> names(){
        return list;
    }

    public static List<String> names(int size){
        return new ArrayList<>(select(size).keySet());
    }
}

 public static void main(String[] args) {
        System.out.println(capitals(3)); // {中国=北京, 日本=东京, 泰国=曼谷}
        System.out.println(new HashMap<>(capitals(1))); // {中国=北京}
        System.out.println(names(2)); // [中国, 日本]
        System.out.println(new ArrayList<>(names(1))); // [中国]
        System.out.println(capitals().get("中国")); // 北京
    }

不受尺寸限制的实现

class MyList extends AbstractList<Integer>{
    private int size;

    public MyList(int size) {
        this.size = size;
    }

    @Override
    public Integer get(int index) {
        return index;
    }

    @Override
    public int size() {
        return size;
    }
}


class MyMap extends AbstractMap<Integer,String>{
    private int size;
    private static char[] chars ={'a','b','c','d'};


    public MyMap(int size) {
        this.size = size;
    }

    private  class MyEntry implements Entry<Integer,String>{
        private int index;

        public MyEntry(int index) {
            this.index = index;
        }

        @Override
        public Integer getKey() {
            return index;
        }

        @Override
        public String getValue() {
            return Integer.toString(index)+chars[index%chars.length];
        }

        @Override
        public String setValue(String value) {
           throw new UnsupportedOperationException();
        }
    }

    @Override
    public Set<Entry<Integer, String>> entrySet() {
        Set<Entry<Integer,String>> set = new LinkedHashSet<>();
        for (int i = 0; i < size; i++) {
            set.add(new MyEntry(i));
        }
        return set;
    }
}

 public static void main(String[] args) {
        System.out.println(new MyList(20));
     // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
      System.out.println(new MyMap(10));
     // {0=0a, 1=1b, 2=2c, 3=3d, 4=4a, 5=5b, 6=6c, 7=7d, 8=8a, 9=9b}
    }

17.3 Collection的功能方法

方法名说明
boolean add(E e)向集合添加元素e,若指定集合元素改变了则返回true
boolean addAll(Collection<? extends E> c)把集合C中的元素全部添加到集合中,若指定集合元素改变返回true
void clear()清空所有集合元素
boolean contains(Object o)判断指定集合是否包含对象o
boolean containsAll(Collection<?> c)判断指定集合是否包含集合c的所有元素
boolean isEmpty()判断指定集合的元素size是否为0
boolean remove(Object o)删除集合中的元素对象o,若集合有多个o元素,则只会删除第一个元素
boolean removeAll(Collection<?> c)删除指定集合包含集合c的元素
boolean retainAll(Collection<?> c)从指定集合中保留包含集合c的元素,其他元素则删除
int size()集合的元素个数
T[] toArray(T[] a)将集合转换为T类型的数组
   public static void main(String[] args) {
        Collection<String> c1 = new ArrayList<>();
        Collection<String> c2 = new ArrayList<>(Arrays.asList("e","f","g"));
        // 添加
        boolean flag = c1.add("a");
        System.out.println(flag); // true
        System.out.println(c1); // [a]
        flag = c1.addAll(c2);
        System.out.println(c1); // [a, e, f, g]

        flag = c1.remove("a"); // true
        System.out.println(flag); // [e, f, g]
        System.out.println(c1);
        flag = c1.removeAll(c2);
        System.out.println(c1); // []

        // 包含
        flag = c1.contains("a");
        System.out.println(flag); // false
        flag = c1.containsAll(c2);
        System.out.println(flag); // false

        // 是否为空
        flag = c1.isEmpty();
        System.out.println(flag); // true

        // 长度
        c1.add("b");
        int size = c1.size();
        System.out.println(size); // 1

        // 转数组
        String[] strings1 = new String[5];
        System.out.println(strings1); // [Ljava.lang.String;@27c170f0
        String[] strings2 = c1.toArray(strings1);
        System.out.println(strings2); // [Ljava.lang.String;@27c170f0 指向同一个地址
        System.out.println(Arrays.toString(strings1)); // [b, null, null, null, null]

        // 清除元素
        c1.clear();
        System.out.println(c1); // []
    }

17.4 可选操作

注意: 这里可能有些绕。

在Collection接口中添加和移除的方法都是可选操作;

什么是可选操作:实现类并没有提供对应方法的功能定义,就Collection接口而言,就是在实现中抛出UnsupportOperationException异常;而该定义,只是在实现层次的操作,并没有提供语法层次的操作。

为什么会出现可选操作?防止接口爆炸,如容器可能分为只读的,可修改的,可添加的,可删除的等,但是大家除add()和remove()外有很多共同点,现在就需要一个抽象实现类,来实现这些方法,但是在抽象实现类中的add()方法并不能有具体所指,所以就通过抛异常的方式来表现(为什么不使用抽象方法呢?多个子类可能add()方法不能使用,使用抽象方法,就不能代码复用)。

大部分可选操作都发生在抽象实现类中,如AbstractList

  public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

最常见的未获支持的操作,都是背后的容器尺寸固定问题。

 public static void main(String[] args) {
       List<String> list = Arrays.asList("a","b");
       list.set(1,"c");
       list.add("d"); // Exception in thread "main" java.lang.UnsupportedOperationException
    }

asList()方法返回的List是Arrays的内部类,该类没有重写add()方法,只能修改,不能添加。

   public static void main(String[] args) {
       List<String> list = Arrays.asList("a","b");
       list = Collections.unmodifiableList(list);
       list.set(1,"c"); // Exception in thread "main" java.lang.UnsupportedOperationException
    }
// 只读的List

17.5 List的功能方法

  // List的基本方法
    public static void basicTest(List<String> list){
        // 添加元素
        list.add("a");
        list.add(1,"b");
        list.addAll(Arrays.asList("c","d"));
        list.addAll(4,Arrays.asList("e","f"));

        // 是否包含
        boolean contains = list.contains(1);
        contains = list.containsAll(Arrays.asList("c","d"));

        // 获得元素
        String s = list.get(0);
        int index = list.indexOf("a");

        // 是否为空
        boolean isEmpty =list.isEmpty();

        // 获得迭代器
        Iterator<String> iterator = list.iterator();
        // listIterator针对List的迭代器,有更多的功能
        ListIterator<String> listIterator = list.listIterator();
        // 从指定索引处迭代集合
        listIterator = list.listIterator(3);
        // 前面是否有元素
        boolean flag = listIterator.hasPrevious();
        // 下一个索引
        listIterator.nextIndex();
        // 前一个元素
        listIterator.previous();
        // 前一个索引
        listIterator.previousIndex();

        // 删除
        list.remove(1);
        list.remove("c");
        list.removeAll(Arrays.asList("e","f"));

        // 修改
        list.set(0,"z");

        // 移除不在目标集合中的元素
        list.retainAll(Arrays.asList("c","d"));

        int size = list.size();
        list.clear();
    }

    // LinkedList的特殊用法
    public static void testLinkedList(LinkedList<String> linkedList){
        // 在头部操作元素
        linkedList.addFirst("a");
        linkedList.getFirst();
        linkedList.removeFirst();

        // 在尾部操作元素
        linkedList.addLast("b");
        linkedList.removeLast();
    }

17.6 Set和存储顺序

类型描述
Set(interface)存入Set的每个元素都必须是唯一的,因为set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set和Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet *为快速查找而设计的Set。存入HashSet的元素必须定义hashCode()。
TreeSet保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口。
LinkedHashSet具有HashSet的查询速度,且内部使用链表维护元素顺序(插入顺序)。于是在使用迭代器遍历Set时,结果会按元素插入顺序显示,元素也必须实现hashCode()方法。

必须为Set创建equals()方法,只有HashSet和LinkHashSet需要hashCode()方法。但是良好的编程风格应该是重写equals()时跟着重写hashCode()。


class SetType{
    int i;

    public SetType(int i) {
        this.i = i;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SetType setType = (SetType) o;
        return i == setType.i;
    }

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

class HashSetType extends SetType{
    public HashSetType(int i) {
        super(i);
    }

    @Override
    public int hashCode() {
        return i;
    }
}

class TreeSetType extends SetType implements Comparable<TreeSetType>{
    public TreeSetType(int i) {
        super(i);
    }

    @Override
    public int compareTo(TreeSetType o) {
        // 为什么不使用加减运算?
        // 因为当正数-负数时可能会超过int的范围
        return i > o.i?-1:(i == o.i?0:1);
    }
}

// 工具类
class TypesForSet{
    // 填充set
    private static <T> Set<T> fill(Set<T> set,Class<T> c){
        try {
            for (int i = 0; i < 10; i++) {
                set.add(c.getConstructor(int.class).newInstance(i));
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return set;
    }

    public static <T> void test(Set<T> set,Class<T> c){
        fill(set,c);
        fill(set,c);
        System.out.println(set);
    }

    public static void main(String[] args) {
        // 按照hash函数计算后的排序
        test(new HashSet<>(),HashSetType.class); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        // 按照比较后的排序
        test(new TreeSet<>(),TreeSetType.class); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        // 没有重写hashcode(),可以看到Set认为他们不相等
        test(new HashSet<>(),SetType.class); // [0, 8, 9, 3, 8, 2, 6, 9, 3, 6, 4, 5, 4, 5, 7, 2, 7, 1, 0, 1]
        // 没有实现Comparable接口而存入TreeSet会报错
        test(new TreeSet<>(),SetType.class); // ClassCastException: com.container.SetType cannot be cast to java.lang.Comparable
    }
}

SortedSet

SortedSet中的元素保证处于排序状态,它是按对象的比较函数排序。

    public static void main(String[] args) {
        SortedSet<String> sortedSet = new TreeSet<>();
        // 返回SortedSet的比较函数
        Comparator<? super String> comparator = sortedSet.comparator();
        Collections.addAll(sortedSet,"one two three four five six".split(" "));
        System.out.println(sortedSet); // [five, four, one, six, three, two]
        String first = sortedSet.first();
        String last = sortedSet.last();
        System.out.println("first:"+first+" "+"last:"+last); // first:five last:two
        Iterator<String> iterator = sortedSet.iterator();
        // 返回子Set
        SortedSet<String> subSet = sortedSet.subSet("four", "six");
        // 返回到。。为止的Set
        SortedSet<String> headSet = sortedSet.headSet("one");
        // 返回从。。开始的Set
        SortedSet<String> tailSet = sortedSet.tailSet("one");
    }

17.7 队列

除了并发应用Queue目前在JDK5中仅有两个实现:LinkedList和PriorityQueue,他们的差异在于排序行为而不是性能。

public class Test1 {
    public static void main(String[] args) {
        test(new LinkedList<>()); // one two three four 
        test(new PriorityQueue<>()); // four one three two // 按优先级排序
        test(new ArrayBlockingQueue<>(5)); // one two three four 
        test(new ConcurrentLinkedQueue<>()); // one two three four
    }

    public static void test(Queue<String> queue){
        for(String s:"one two three four".split(" ")){
            queue.offer(s);
        }
        while (queue.peek() != null){
            System.out.print(queue.remove()+" ");
        }
        System.out.println();
    }
}

优先级队列

// 待做列表
class ToDoList extends PriorityQueue<ToDoList.ToDoItem>{
    static class ToDoItem implements Comparable<ToDoItem>{
        // 做的事
        private String str;
        // 主优先级
        private char priority;
        // 次优先级
        private int secondPriority;

        public ToDoItem(String str, char priority, int secondPriority) {
            this.str = str;
            this.priority = priority;
            this.secondPriority = secondPriority;
        }

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

        @Override
        public String toString() {
            return priority +" "+secondPriority+" "+str;
        }
    }

    // 添加待做事项
    public boolean add(String str,char priority,int secondPriority) {
        return super.add(new ToDoItem(str,priority,secondPriority));
    }

    public static void main(String[] args) {
        ToDoList toDoList = new ToDoList();
        toDoList.add("学英语",'A',3);
        toDoList.add("敲代码",'A',1);
        toDoList.add("玩游戏",'B',1);
        toDoList.add("睡觉",'B',2);
        while (!toDoList.isEmpty()){
            System.out.println(toDoList.remove());
        }
    }
}

双向队列

双向队列就像是一个队列,但是可以在两端添加或移除元素,LinkedList支持双向队列的方法,在JDK6中引入了Deque接口来表示双向队列,但是该接口并不常用。

17.8 理解Map

映射表的基本思想是维护键-值关联,其有多种实现,实现的行为特性各不相同,表现在效率/键值对的保存/呈现次序/对象的保存周期/在多线程中的工作/如何判定键的等价。


public class AssociativeArray<K,V> {
	
	public static void main(String[] args) {
		AssociativeArray<String, String> map = new AssociativeArray<String, String>(3);
		map.put("a", "1");
		map.put("b", "2");
		System.out.println(map.get("b"));
		System.out.println(map);
	}
	private Object[][] pairs;
	private int index;
	public  AssociativeArray(int length) {
		pairs = new Object[length][2];
	}
	
	public void	put(K key,V value) {
		if (index > pairs.length) {
			throw new RuntimeException("超长");
		}
		pairs[index++] = new Object[]{key,value};
	}
	
	public V get(K key) {
		for(int i = 0;i<pairs.length;i++){
			if (key.equals(pairs[i][0])) {
				return (V)pairs[i][1];
			}
		}
		return null;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		for(int i = 0;i<pairs.length;i++){
			sb.append(pairs[i][0]).append(":").append(pairs[i][1]);
			if (i<pairs.length-1) {
				sb.append(",");
			}
		}
		sb.append("]");
		return sb.toString();
	}
}

性能

当在get()中使用线性搜索时,执行速度会相当地慢,而HashMap使用散列码,来取代对键的缓慢搜索。

public class Test1 {
    public static void main(String[] args) {
        test(add(new HashMap<>()));
        System.out.println("---");
        test(add(new ConcurrentHashMap<>()));
    }
    public static Map<Integer, String> add(Map<Integer, String> map){
        map.put(0,"a");
        map.put(1,"b");
        return map;
    }

    public static void test(Map<Integer, String> map){
        System.out.println(map.getClass().getSimpleName()); // HashMap
        // 添加集合
        map.putAll(Collections.singletonMap(2,"c"));
        // 获得key
        System.out.println(map.keySet()); // [0, 1, 2]
        // 获得values
        // 注意:values()返回一个Collection,其中的值有任何变化,也会反映到map中
        System.out.println(map.values()); // [a, b, c]
        // 是否包含key
        System.out.println(map.containsKey(1)); // true
        System.out.println(map.get(1)); // b
        // 是否包含value
        System.out.println(map.containsValue("c")); // true
        // key迭代器
        Iterator<Integer> iterator = map.keySet().iterator();
        System.out.println(map.remove(2)); // c
        map.clear();
        System.out.println(map); // {}
    }
}

SortedMap

SortedMap可以确保键处于排序状态,目前仅有一个实现:TreeMap,其也是Map中可以获得子Map的实现。

 public static void main(String[] args) {
        SortedMap<String,String> sortedMap = new TreeMap<>();
        sortedMap.put("one","a");
        sortedMap.put("two","b");
        sortedMap.put("four","d");
        // 排序函数
        Comparator<? super String> comparator = sortedMap.comparator();
        // 首尾Key
        String firstKey = sortedMap.firstKey();
        String lastKey = sortedMap.lastKey();
        // 获得子Map
        sortedMap.subMap("four","one");
        sortedMap.headMap("two");
        sortedMap.tailMap("four");
    }

LinkedHashMap

为了提高速度LinkedHashMap会散列化所有元素,但是在遍历键值对时,却以插入顺序返回;另外,在构造时可以设定基于最少使用算法。

public class Test1 {
    public static void main(String[] args) {
        Map<Integer, String> map = add(new LinkedHashMap<>());
        System.out.println(map); // {3=c, 1=a, 2=b}
        map = add(new LinkedHashMap<>(16,0.75f,true));
        System.out.println(map); // {3=c, 1=a, 2=b}
        map.get(3);
        // 在使用后,就会被排到后边
        System.out.println(map); // {1=a, 2=b, 3=c}
    }

    public static Map<Integer, String> add(Map<Integer, String> map){
        map.put(3,"c");
        map.put(1,"a");
        map.put(2,"b");
        return map;
    }

}

17.9 散列和散列码

// 土拨鼠
class Groundhog{
    private int number;

    public Groundhog(int number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "土拨鼠"+number+"号";
    }
}

// 天气预报
class Prediction{
    private Random random = new Random();
    private boolean flag = random.nextDouble()>0.5;

    @Override
    public String toString() {
        return flag?"温暖的春天":"寒冷的冬天";
    }
}

public static void main(String[] args) {
        Map<Groundhog,Prediction> map = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            map.put(new Groundhog(i),new Prediction());
        }
        System.out.println(map);
    // {土拨鼠0号=温暖的春天, 土拨鼠1号=温暖的春天, 土拨鼠2号=寒冷的冬天, 土拨鼠3号=寒冷的冬天, 土拨鼠4号=寒冷的冬天}
        System.out.println(map.containsKey(new Groundhog(2))); // false
    }

注意: 可以看到通过new Groundhog(2)并不能找到对应的value,因为这里使用的是Object的hashChode()生成散列码,默认使用对象的地址计算,这样,当然不会找到。

同时,我们还需要重写equals()方法。

正确的 equals()方法必须满足下列5 个条件:

自反性:对任意 x,x.equals\(x\)一定返回true。
对称性:对任意x 和y,如果y.equals\(x\)返回true,则x.equals\(y\)也返回true。
传递性:对任意x,y,z,如果有x.equals\(y\)返回ture,y.equals\(z\)返回true,则x.equals\(z\)一定返回true。
一致性:对任意 x 和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals\(y\)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false。
对任何不是 null 的x,x.equals\(null\)一定返回false。
// 重写equals和hashCode
class Groundhog{
    private int number;

    public Groundhog(int number) {
        this.number = number;
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof/*隐含检查了o是否为null*/ Groundhog && ((Groundhog) o).number == number;
    }

    @Override
    public int hashCode() {
        return number;
    }

    @Override
    public String toString() {
        return "土拨鼠"+number+"号";
    }
}
17.9.1 理解hashCode()

使用散列的目的:使用一个对象查找另一个对象。

// 使用一对ArrayList实现一个Map
class SlowMap<K,V> extends AbstractMap<K,V>{
    private List<K> keys = new ArrayList<>();
    private List<V> values = new ArrayList<>();

    @Override
    public V get(Object key) {
        if (keys.contains(key)) {
            return values.get(keys.indexOf(key));
        }else {
            return null;
        }
    }

    // 返回旧值或null
    @Override
    public V put(K key, V value) {
        V oldValue = get(key);
        if (!keys.contains(key)){
            keys.add(key);
            values.add(value);
        }else {
            values.set(keys.indexOf(key),value);
        }
        return oldValue;
    }

    // 完整的Map实现需要实现entrySet方法,该方法返回值是一个Entry的Set,需要实现Entry接口
    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Map.Entry<K,V>> set = new HashSet<>();
        Iterator<K> kIterator = keys.iterator();
        Iterator<V> vIterator = values.iterator();
        while (kIterator.hasNext()){
            set.add(new MapEntry(kIterator.next(),vIterator.next()));
        }
        return set;
    }

    private class MapEntry implements Map.Entry<K,V>{
        private K k;
        private V v;

        public MapEntry(K k, V v) {
            this.k = k;
            this.v = v;
        }

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }

        @Override
        public V setValue(V value) {
            v = value;
            return v;
        }

        @Override
        public boolean equals(Object o) {
            // 必须检查键和值,o可能不是该类的实例
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Entry entry = (Entry) o;
            return (k == null ? entry.getKey() == null:k.equals(entry.getKey()))&&
                    (v == null ? entry.getValue() == null : v.equals(entry.getValue()));
        }

        // 这里提供了一个hashCode简单的实现
        // 虽然可以使用,但是并不恰当
        @Override
        public int hashCode() {
            return (k == null?0:k.hashCode()) ^ (v == null?0:v.hashCode());
        }
    }
}
17.9.2 为速度而散列

SlowMap使用简单的线性查询,而线性查询是最慢的。

散列码: 数组并不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标,这个数字就是散列码。

冲突: 为解决数组容量被固定的问题,不同的键可以产生相同的下标,这可能会产生冲突。

// 简单的散列Map
class SimpleHashMap<K, V> extends AbstractMap<K, V> {
    // 确定数组大小
    private final int SIZE = 997;
    // 底层储存结构,数组+链表
    private LinkedList<MapEntry>[] buckets/*水桶*/ = new LinkedList[SIZE];

    @Override
    public V get(Object key) {
        // 获得数组下标
        int index = key.hashCode() % SIZE;
        // 该位置是否有链表
        if (buckets[index] == null) {
            return null;
        }
        // 遍历链表
        for (MapEntry entry : buckets[index]) {
            if (entry.getKey().equals(key)) {
                return entry.getValue();
            }
        }
        return null;
    }

    // 返回旧值或null
    @Override
    public V put(K key, V value) {
        // 获得数组下标
        int index = key.hashCode() % SIZE;
        // 保证该位置有链表
        if (buckets[index] == null) {
            buckets[index] = new LinkedList<>();
        }
        V oldValue = null;
        // 修改或添加的标识
        boolean flag = false;
        for (MapEntry entry : buckets[index]) {
            // 如果含有该值
            if (entry.getKey().equals(key)) {
                oldValue = entry.getValue();
                // 修改值并标识为修改
                entry.setValue(value);
                flag = true;
                break;
            }
        }
        // 如果非修改,就添加元素
        if (!flag){
            buckets[index].add(new MapEntry(key,value));
        }
        // 返回旧值或null
        return oldValue;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Map.Entry<K, V>> set = new HashSet<>();
        // 双重遍历,将实体添加到Set
        for (LinkedList<MapEntry> bucket : buckets) {
            // 如果没有链表,跳过
            if (bucket == null) {
                continue;
            }
            for (MapEntry entry : bucket) {
                set.add(entry);
            }
        }
        return set;
    }

    private class MapEntry implements Map.Entry<K, V> {
        private K k;
        private V v;

        public MapEntry(K k, V v) {
            this.k = k;
            this.v = v;
        }

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }

        @Override
        public V setValue(V value) {
            v = value;
            return v;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Entry entry = (Entry) o;
            return (k == null ? entry.getKey() == null : k.equals(entry.getKey())) &&
                    (v == null ? entry.getValue() == null : v.equals(entry.getValue()));
        }

        @Override
        public int hashCode() {
            return (k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode());
        }
    }
}

 public static void main(String[] args) {
        Map<String,String> map = new SimpleHashMap<>();
        // map.put(null,"a"); // 这里有一个bug,既即不能以null作键
        map.put("one","a");
        map.put("two","b");
        System.out.println(map); // {one=a, two=b}
        System.out.println(map.get("two")); // b
    }

散列表中的槽位通常称为桶位,因此将底层结构命名为buckets。桶的数量通常使用质数,但近来实验表明,2的整数次方才是最优的选择,对处理器而言除法与求余数是最慢的操作,近来的选择,可用掩码代替除法。

17.9.3 覆盖hashCode()

注意事项:

  • 无法控制bucket数组的具体下标值;
  • put和get时得到的hashCode必须相同;
  • 不能依赖于易变的数据,当数据产生变化时,会产生不同的hash值;
  • 不能依赖于唯一的值,否则使用数组+链表就没有什么意义了。
  • hashCode应该产生分布均匀的散列码,否则某些区域负载过重。

计算指导

  1. 给int变量result赋予一个常量;

  2. 为对象内有意义的域f计算出散列码c;
    | 域类型 | 计算 |
    | — | — |
    | boolean | c = (f?0:1) |
    | byte/char/shot/int | c=(int)f |
    | long | c=(int)(f^(f>>>32)) |
    | float | c =Float.floatToIntBits(f) |
    | double | long l = Double.doubleToLongBits(f);c=(int)(f^(f>>>32)) |
    | Object | c =f.hashCode() |
    | 数组 | 对每个元素用上述方法 |

合并计算:result = 37 \*result +c;
返回result;
//简单示例
class CountedString {
    // 创建的string对象都存储在这里
    private static List<String> list = new ArrayList<>();
    private String s;
    // 创建的第几个该对象
    private int id;

    public CountedString(String s) {
        this.s = s;
        list.add(s);
        for (String s1 : list) {
            if (s1.equals(s)) {
                id++;
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof CountedString && ((CountedString) o).s.equals(s) && id == ((CountedString) o).id;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 37 * result + s.hashCode();
        result = 37 * result + id;
        return result;
    }

    @Override
    public String toString() {
        return "String:" + s + " " + "id:" + " " + id + "hashCode:" + hashCode();
    }
}

public static void main(String[] args) {
        Map<CountedString, Integer> map = new HashMap<>();
        CountedString[] countedStrings = new CountedString[3];
        for (int i = 0; i < 3; i++) {
            countedStrings[i] = new CountedString(new String("hi"));
            map.put(countedStrings[i],i);
        }
        System.out.println(map);
    // {String:hi id: 2hashCode:146448=1, String:hi id: 3hashCode:146449=2, String:hi id: 1hashCode:146447=0}
        for (int i = 0; i < countedStrings.length; i++) {
            System.out.println(countedStrings[i]);
            System.out.println(map.get(countedStrings[i]));
            /*
            String:hi id: 1hashCode:146447
            0
            String:hi id: 2hashCode:146448
            1
            String:hi id: 3hashCode:146449
            2
            */
        }
    }
/*
可以看到,正因为hashcode不同,在map中所有元素都被存储了
这里存在的问题时,不可能产生相同的对象
*/
// 另外一个示例,重点在比较
class Individual implements Comparable<Individual>{
    private static int counter;
    private final int id = counter++;
    private String name;

    public Individual(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "宠物:"+name+id+"号 ";
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof Individual && id == ((Individual) o).id;
    }

    @Override
    public int hashCode() {
        int result = 17;
        if (name != null) {
            result = result *37+name.hashCode();
        }
        result = result*37+id;
        return result;
    }

    // 有一个排序序列,如果name不同则按name排序,name相同就按id排序
    @Override
    public int compareTo(Individual o) {
        if (name != null && o.name != null) {
            int compare = name.compareTo(o.name);
            if ( compare != 0) {
                return compare;
            }
        }
        return id > o.id ? 1 :(id == o.id ? 0:-1);
    }
}

 public static void main(String[] args) {
        Set<Individual> set = new TreeSet<>();
        set.add(new Individual("cat"));
        set.add(new Individual("dog"));
        set.add(new Individual("cat"));
        System.out.println(set);
     // [宠物:cat0号 , 宠物:cat2号 , 宠物:dog1号 ]
    }

17.10 选择接口的不同实现

容器实际上只有四种:Map/List/Set/Queue。

容器之间的区别是背后用什么数据结构实现的。

我们可以根据实际使用的需求选择不同的实现。

17.10.1 性能测试框架
// 测试基类
public abstract class Test<C> {
    // 测试名字
    public String name;

    public Test(String name) {
        this.name = name;
    }

    /**
     * 每个被测的容器应当覆盖该方法
     * @param container 测试的容器
     * @param tp 测试参数
     * @return 测试的数量
     */
    public abstract int test(C container,TestParam tp);
}

// 测试参数
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 < result.length; i++) {
            // 指定容器的长度和迭代的次数
            // values偶数位是长度,奇数位是迭代次数
            result[i] = new TestParam(values[n++],values[n++]);
        }
        return result;
    }

    public static TestParam[] array(String[] values){
        int[] ints = new int[values.length];
        for (int i = 0; i < ints.length; i++) {
            // 将字符串解码为整数,接收八进制和十六进制
            ints[i] = Integer.decode(values[i]);
        }
        return array(ints);
    }
}

// 测试者
// 核心方法
public class Tester<C> {
    // 字段宽度
    public static int fieldWidth = 8;
    // 被测容器长度宽度
    private static int sizeWidth = 5;
    // 长度的格式化
    private static String sizeField = "%" + sizeWidth + "s";

    // 测试方法的格式化
    private static String stringField() {
        return "%" + fieldWidth + "s";
    }

    // 测试数据的格式化
    private static String numberField() {
        return "%" + fieldWidth + "d";
    }

    // 默认测试参数对象集
    public static TestParam[] defaultParams = TestParam.array(10, 5000, 100, 5000, 1000, 5000, 10000, 500);
    // 测试参数集
    private TestParam[] paramList = defaultParams;
    // 测试集合
    private List<Test<C>> tests;
    // 测试容器
    protected C container;
    // 标题
    private String headline = "";

    // 初始化函数
    protected C initialize(int size) {
        return container;
    }

    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 headline) {
        this.headline = headline;
    }


    // 打印头部信息
    protected void displayHeader() {
        // 标题总宽度
        int width = fieldWidth * tests.size() + sizeWidth;
        // 除标题文字的宽度
        int dashLength = width - headline.length() - 1;
        // 组合标题
        StringBuilder head = new StringBuilder(width);
        for (int i = 0; i < dashLength / 2; i++) {
            head.append("-");
        }
        head.append(" ").append(headline).append(" ");
        for (int i = 0; i < dashLength / 2; i++) {
            head.append("-");
        }
        // 打印标题
        System.out.println(head);
        // 打印类目
        System.out.format(sizeField, "size");
        for (Test<C> test : tests) {
            System.out.format(stringField(), test.name);
        }
        System.out.println();
    }

    // 核心测试方法
    protected void timedTest() {
        // 打印头部
        displayHeader();
        // 遍历测试参数集
        for (TestParam testParam : paramList) {
            System.out.format(sizeField, testParam.size);
            // 遍历测试集
            for (Test<C> test : tests) {
                // 初始化
                C c = initialize(testParam.size);
                // 以纳秒为单位
                long start = System.nanoTime();
                int reps = test.test(c, testParam);
                long duration = System.nanoTime() - start;
                long timePerRep = duration / reps;
                System.out.format(numberField(), timePerRep);
            }
            // 注意换行位置
            System.out.println();
        }
    }

    // 对外提供的方法
    public static <C> void run(C cntnr, List<Test<C>> tests) {
        new Tester<C>(cntnr, tests).timedTest();
    }

    public static <C> void run(C cntnr, List<Test<C>> tests, TestParam[] testParams) {
        new Tester<C>(cntnr, tests, testParams).timedTest();
    }
}
17.10.2 对List的选择
public class ListPerformance {
    static Random random = new Random();
    static int reps = 1000;
    // ArrayList测试集
    static List<Test<List<Integer>>> tests = new ArrayList<>();
    // LinkedList测试集
    static LinkedList<Test<LinkedList<Integer>>> qtests = new LinkedList<>();

    static {
        tests.add(new Test<List<Integer>>("add") {
            @Override
            public int test(List<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.clear();
                    for (int j = 0; j < tp.size; j++) {
                        list.add(j);
                    }
                }
                return tp.loops * tp.size;
            }
        });
        tests.add(new Test<List<Integer>>("get") {
            @Override
            public int test(List<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.get(random.nextInt(list.size()));
                }
                return tp.loops;
            }
        });

        tests.add(new Test<List<Integer>>("set") {
            @Override
            public int test(List<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.set(random.nextInt(list.size()),i);
                }
                return tp.loops;
            }
        });
        tests.add(new Test<List<Integer>>("remove") {
            @Override
            public int test(List<Integer> list, TestParam tp) {
                for (int i = 0; i < list.size(); i++) {
                    if (list.size() >2){
                        list.remove(1);
                    }
                }
                return list.size();
            }
        });
         tests.add(new Test<List<Integer>>("insert") {
            @Override
            public int test(List<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.add(8,i);
                }
                return tp.loops;
            }
        });

        qtests.add(new Test<LinkedList<Integer>>("addFirst") {
            @Override
            public int test(LinkedList<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.clear();
                    for (int j = 0; j < tp.size; j++) {
                        list.addFirst(i);
                    }
                }
                return tp.loops*tp.size;
            }
        });
        qtests.add(new Test<LinkedList<Integer>>("addLast") {
            @Override
            public int test(LinkedList<Integer> list, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    list.clear();
                    for (int j = 0; j < tp.size; j++) {
                        list.addLast(i);
                    }
                }
                return tp.loops*tp.size;
            }
        });
    }
}

// List测试
public class ListTester extends Tester<List<Integer>> {
    public ListTester(List<Integer> container, List<Test<List<Integer>>> tests) {
        super(container, tests);
    }
    // 重写初始化方法
    // 本方法也很重要,timedTest每次在执行Test.test时都必须初始化,否则,前次执行的结果会影响后执行的结果
    @Override
    protected List<Integer> initialize(int size) {
        container.clear();
        for (int i = 0; i < size; i++) {
            container.add(i);
        }
        return container;
    }

    public static void run(List<Integer> list,List<Test<List<Integer>>> tests){
        new ListTester(list,tests).timedTest();
    }
    
      public static void main(String[] args) {
        ListTester.run(new ArrayList<>(),ListPerformance.tests);
          /*
       ----------------- ArrayList -----------------
         size     add     get     set  remove  insert
           10      63     340     224    3880     392
          100      16     138      83     516     341
         1000      10      53      78     467     293
        10000       7      78     217     917    1034
        100000      11     392     410    8623   10762
		*/
        ListTester.run(new LinkedList<>(),ListPerformance.tests);
        /*
        ----------------- LinkedList -----------------
         size     add     get     set  remove  insert
           10      81     133     124    2060     199
          100      37      88     120     266      58
         1000      15     387     405     272      62
        10000       9    6323    6115      53      39
        100000      25  105806   75586      29     162
        */
        ListTester.run(new LinkedList<>(),ListPerformance.qtests);
          /*
        ----- LinkedList -----
         sizeaddFirst addLast
           10      59      41
          100      15      20
         1000      16      18
        10000      31      16
        100000      10      10
		*/
    }
}
/*
从测试数据看到,随着容器的长度变大,LinkedList查询的性能明显下降,而ArrayList则是插入的性能明显下降;同时也可以看到,其实在万级以内,差距并不明显
*/
17.10.3 微基准测试的危险

微基准测试理解:对一些小的东西,小数据量的测试。

做这类测试时,应当关注点要单一,但是不要从结果中获得太多的结论,这类结果很容易受其他因素的影响。

17.10.4 对Set的选择
public class SetPerformance {
    static List<Test<Set<Integer>>> list = new ArrayList<>();
    static {
        list.add(new Test<Set<Integer>>("add") {
            @Override
            public int test(Set<Integer> set, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    set.clear();
                    for (int j = 0; j < tp.size; j++) {
                        set.add(j);
                    }
                }
                return tp.loops*tp.size;
            }
        });
        list.add(new Test<Set<Integer>>("contains") {
            @Override
            public int test(Set<Integer> set, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    set.clear();
                    for (int j = 0; j < tp.size; j++) {
                        set.contains(j);
                    }
                }
                return tp.loops*tp.size;
            }
        });
    }
    
      public static void main(String[] args) {
        Tester.run(new HashSet<>(),SetPerformance.list);
        Tester.run(new TreeSet<>(),SetPerformance.list);
        Tester.run(new LinkedHashSet<>(),SetPerformance.list);
    }
    /*
    ------ HashSet ------
     size     addcontains
       10      92      58
      100      36      23
     1000      20       7
    10000      18       2
    100000      22       2
    ------ TreeSet ------
     size     addcontains
       10     194      57
      100      53       2
     1000      51       3
    10000      89       4
    100000     145       3
    --- LinkedHashSet ---
     size     addcontains
       10     140      99
      100      34       9
     1000      14      13
    10000      31       8
    100000      15       7
    */
}

本地测试的结果和书上的差距比较大;总体而言,一般选择HashSet,需要排序,则选择TreeSet,如果需要按插入排序,则选择LinkedHashSet。

17.10.5 对Map的选择
public class MapPerformance {
    static List<Test<Map<Integer,Integer>>> tests = new ArrayList<>();
    static {
        tests.add(new Test<Map<Integer, Integer>>("put") {
            @Override
            public int test(Map<Integer, Integer> map, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    for (int j = 0; j < tp.size; j++) {
                        map.put(j,i);
                    }
                }
                return tp.loops * tp.size;
            }
        });
        tests.add(new Test<Map<Integer, Integer>>("get") {
            @Override
            public int test(Map<Integer, Integer> map, TestParam tp) {
                for (int i = 0; i < tp.loops; i++) {
                    for (int j = 0; j < tp.size; j++) {
                        map.put(j,i);
                    }
                }
                return tp.loops * tp.size;
            }
        });

    }
}

    public static void main(String[] args) {
        Tester.run(new HashMap<>(),MapPerformance.tests);
        /*
        ------ HashMap ------
         size     put     get
           10     119      63
          100      24      27
         1000      21      15
        10000      17      12
        100000      15      19
        */
        Tester.run(new TreeMap<>(),MapPerformance.tests);
        /*
        ------ TreeMap ------
         size     put     get
           10     222     192
          100      38      33
         1000      86      96
        10000      86      79
        100000     123     112
        */
        Tester.run(new LinkedHashMap<>(),MapPerformance.tests);
        /*
        --- LinkedHashMap ---
         size     put     get
           10      75      99
          100      29      12
         1000      13      12
        10000      14      13
        100000      13      12
        */
    }

相关术语

容量:容器中存放元素的最大数;

尺寸:容器中当前存储的项数;

负载因子:尺寸/容量。

17.11 实用方法

Collections工具类中的实用方法

方法说明
checkedCollection(Collection,Class)检查类型是否符合
max/min(Collection)返回最大/最小元素
max/min(Collection,Comparator)指定比较器,返回
indexOfSubLsit(List source,List target)target在source第一次出现的位置
lastIndexOfSubList(List source,List target)最后一次位置
replaceAll(List,T oldVal,T newVal)新的替换所有旧的元素
reverse(List)反转元素次序
reverseOrder()返回逆转集合排序的比较器
rotate(List,int distance)元素向后移动distance个位置,末尾循环到前面
shuffle(List)打乱排序
sort(Lsit)排序
copy(Lsit dest,List src)src中的元素复制到dest
swap(List,int i,int j)交换指定位置的元素
fill(List,T x)用x替换所有
nCopies(int n,T x)返回n大小的List,不可改变,元素指向x
disjoint(Collection,Collection)没有相同的元素,返回true
frequency(Collection,Object x)包含x的个数
emptyLsit()返回不可变空List
singleton(T x)返回不可变Set,元素是x
list(Enumeration)转换旧式代码为Array List
enumeration(Collection)转为旧式代码
import static java.util.Collections.*;

public class Test1 {
    static <T> void print(T t) {
        System.out.println(t);
    }
    static List<String> list = Arrays.asList("one Two three Four five six one".split(" "));

    public static void main(String[] args) {
        print(list); // [one, Two, three, Four, five, six, one]
        print(disjoint(list, singletonList("Four"))); // false
        print(max(list)); // three
        print(max(list, String.CASE_INSENSITIVE_ORDER)); // Two
        print(indexOfSubList(list, singletonList("one"))); // 0
        print(lastIndexOfSubList(list, singletonList("one"))); // 6
        replaceAll(list, "one", "YO");
        print(list); // [YO, Two, three, Four, five, six, YO]
        reverse(list);
        print(list); // [YO, six, five, Four, three, Two, YO]
        rotate(list, 2);
        print(list); // [Two, YO, YO, six, five, Four, three]
        copy(list, Arrays.asList("1 2".split(" ")));
        print(list); // [1, 2, YO, six, five, Four, three]
        swap(list, 0, list.size() - 1);
        print(list); // [three, 2, YO, six, five, Four, 1]
        shuffle(list, new Random());
        print(list); // [Four, YO, five, 1, 2, three, six]
        fill(list, "X");
        print(list); // // [X, X, X, X, X, X, X]
        List<String> nCopies = nCopies(3, "no");
        print(nCopies); // [no, no, no]
        Enumeration<String> enumeration = enumeration(nCopies);
        Vector<String> vector = new Vector<>();
        // 演示枚举的用法
        while (enumeration.hasMoreElements()){
            vector.addElement(enumeration.nextElement());
        }
        print(list(vector.elements())); // [no, no, no]
    }
}

List的排序和查询

public class Test1 {
    static <T> void print(T t) {
        System.out.println(t);
    }
    // 注意:asList()返回的List不可增删
    static List<String> list = new ArrayList<>(Arrays.asList("one two three four five six one".split(" ")));
    public static void main(String[] args) {
        shuffle(list);
        print(list); // [two, three, one, one, four, six, five]
        ListIterator<String> listIterator = list.listIterator(list.size()-3);
        while (listIterator.hasNext()){
            listIterator.next();
            listIterator.remove();
        }
        print(list); // [two, three, one, one]
        sort(list,String.CASE_INSENSITIVE_ORDER);
        int index = binarySearch(list, list.get(2), String.CASE_INSENSITIVE_ORDER);
        print(index); // 2
    }
}

设定collection或Map不可更改

public class Test1 {
    static <T> void print(T t) {
        System.out.println(t);
    }
    static List<String> list = new ArrayList<>(Arrays.asList("one two three four five six one".split(" ")));
    public static void main(String[] args) {
        Collection<String> collection = unmodifiableCollection(list);
        List<String> unmodifiableList = unmodifiableList(Test1.list);
        Set<Object> unmodifiableSet = unmodifiableSet(new HashSet<>(list));
        SortedSet<String> unmodifiableSortedSet = unmodifiableSortedSet(new TreeSet<>(list));
        Map<Object, Object> unmodifiableMap = unmodifiableMap(new HashMap<>());
    }
}

Collection或Map的同步控制

  public static void main(String[] args) {
        // 同步容器
        Collection<Object> synchronizedCollection = Collections.synchronizedCollection(new ArrayList<>());
        List<Object> synchronizedList = Collections.synchronizedList(new ArrayList<>());
        Set<Object> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
        Map<Object, Object> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
        // 快速报错:当多个进程同时修改同一个容器内容时会报错
        List<String> list = new ArrayList<>();
        Iterator<String> it = list.iterator();
        list.add("a");
        while (it.hasNext()) {
            it.next(); // java.util.ConcurrentModificationException
        }
    }

17.12 持有引用

对于某些对象,当没有普通引用指向它时,我们希望以后还可以继续使用它,但同时当内存耗尽时,也允许释放该对象;Reference就是解决该问题的类,其有三个实现类SoftReference/WeakReference/PhantomReference,其强度由强到弱。

class VeryBig{
    private static final int SIZE = 10000;
    private long[] la = new long[SIZE];
    private String flag;
    public VeryBig(String flag){
        this.flag = flag;
    }

    @Override
    public String toString() {
        return flag;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("清理:"+flag);
    }
}

class References{
    private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<>();
    public static void checkQueue(){
        Reference<? extends VeryBig> poll = rq.poll();
        if (poll != null) {
            System.out.println("在队列中:"+poll.get());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LinkedList<SoftReference<VeryBig>> sa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            sa.add(new SoftReference<>(new VeryBig(String.valueOf(i)),rq));
            System.out.println(sa.getLast());
            /*
            java.lang.ref.SoftReference@27c170f0
            java.lang.ref.SoftReference@5451c3a8
            java.lang.ref.SoftReference@2626b418
            java.lang.ref.SoftReference@5a07e868
            java.lang.ref.SoftReference@76ed5528
            */
            checkQueue();
        }
        LinkedList<WeakReference<VeryBig>> wa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            wa.add(new WeakReference<>(new VeryBig(String.valueOf(i)),rq));
            System.out.println(sa.getLast());
            /*
            java.lang.ref.SoftReference@76ed5528
            java.lang.ref.SoftReference@76ed5528
            java.lang.ref.SoftReference@76ed5528
            java.lang.ref.SoftReference@76ed5528
            java.lang.ref.SoftReference@76ed5528
             */
            checkQueue();
        }
        System.gc();
        /*
        清理:4
        清理:3
        清理:2
        清理:1
        清理:0
         */
        Thread.sleep(1000);
        LinkedList<PhantomReference<VeryBig>> pa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            pa.add(new PhantomReference<>(new VeryBig(String.valueOf(i)),rq));
            System.out.println(sa.getLast());
            checkQueue();
            /*
            java.lang.ref.SoftReference@76ed5528
            在队列中:null
            java.lang.ref.SoftReference@76ed5528
            在队列中:null
            java.lang.ref.SoftReference@76ed5528
            在队列中:null
            java.lang.ref.SoftReference@76ed5528
            在队列中:null
            java.lang.ref.SoftReference@76ed5528
            在队列中:null
             */
        }
    }
}
/*
可以看到,当对象被清理后,仍然可以通过Reference访问到,但是此时对象已为null
*/

WeakHashMap


class Element{
    private String flag;

    public Element(String flag) {
        this.flag = flag;
    }

    @Override
    public String toString() {
        return flag;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Element element = (Element) o;

        return flag != null ? flag.equals(element.flag) : element.flag == null;
    }

    @Override
    public int hashCode() {
        return flag.hashCode();
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println(getClass().getSimpleName()+" "+flag);
    }
}

class Key extends Element{
    public Key(String flag) {
        super(flag);
    }
}

class Value extends Element{
    public Value(String flag) {
        super(flag);
    }
}

  public static void main(String[] args) throws InterruptedException {
        // Key[] keys = new Key[10];
        List<Key> keys = new ArrayList<>();
        WeakHashMap<Key,Value> map = new WeakHashMap<>();
        for (int i = 0; i < 10; i++) {
            Key k = new Key(Integer.toString(i));
            Value v = new Value(Integer.toString(i));
            if (i % 3 == 0) {
                keys.add(k);
            }
            map.put(k, v);
        }
        System.gc();
        /*
        Key 8
        Key 7
        Key 5
        Key 4
        Key 2
        Key 1
         */
        Thread.sleep(1000);
    }
// 可以看到,没回收三个会跳过一个,因为跳过的被普通引用了。而普通引用的对象,在该方法结束前都不会被清理。
// WeakHashMap用来保存WeakReference,其允许垃圾回收机制在该方法未结束前就清理键和值

17.13 Java1.0/1.1的容器

Vector和Enumeration

  public static void main(String[] args) {
      // 老版本唯一可以自我扩展的序列
        Vector<String> vector = new Vector<>(Arrays.asList("a","b","c"));
      // 老版本迭代器
        Enumeration<String> elements = vector.elements();
        while (elements.hasMoreElements()){
            System.out.println(elements.nextElement());
            /*
            a
            b
            c
             */
        }
    }

HashTable

与HashMap非常相似,直接使用HashMap即可。

Stack

 public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        for (String s : "a b c".split(" ")) {
            // 进栈
            stack.push(s);
        }
        // 继承自Vector,具有其的行为
     	// 这是不合理的,因该是组合,而不是继承
        System.out.println(stack.elementAt(2)); // c
        while (!stack.empty()) {
            // 出栈
            System.out.println(stack.pop());
            /*
            c
            b
            a
             */
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值