集合的一些补充

Java集合###

  直接输出Collection是通过AbstractCollection类的toString()方法:

    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

Map类的是通过AbstractMap类的toString()方法###

    public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

  数组是连续的内存空间,查询速度快,增删慢;
  链表充分利用了内存,内存间是不连续的。首尾存储上下一个节点的信息,所以寻址麻烦,查询速度慢。
  哈希表综合了上面两点,一个哈希表,由数组和链表组成。如果一条链表有1000条节点,要查找最后一个节点。用哈希表,将链表分为10组,用一个容量10数组来存储这10组链表的表头节点,这样寻址就快了。
  Hash碰撞,不同的key根据hash算法算出的值可能一样,一样的话就会出现所谓的碰撞。
  HashMap的扩容代价非常大,要生成一个新的桶数组,然后把所有元素重新hash落桶一次,几乎等于重新执行了一次所有元素的put。如果要对map的大小有一个范围的话,可以在构造时给定大小,一个大小设置为:(int) ((float) expectedSize / 0.75F + 1.0F)。并且,key尽量设计简洁。
  HashMap的遍历方式中,使用keySet的两种方式都会遍历两次,所以效率没有使用EntrySet高
  HashMap输出是无序的,这个无序不是说每次遍历结果顺序不一样,而是说输出和输入顺序不一样。
HashMap排序方式:

  • HashMap的按值排序通过Collection 的sort方法
  • TreeMap是按键排序的,默认升序,再把已经排序的TreeMap防务HashMap中
  • 通过keySet取出所有key然后将key排序,再有序将key-value键放到LinkedHashMap中
    HashMap去重:
  • 将HashMap的key-value对调,赋值给一个新的HashMap中,然后再对调。

HashMap线程同步###

//方式一:
	Map<Integer, String> hs = new HashMap<Integer, String>();
		hs.Collections.synchornizedMap(hs);
//方式二:
	ConcurrentHashMap<Integer, String> hs = new ConcurrentHashMap<Integer, String>();

IdentifyHashMap###

  与HashMap基本相似,只是当两个key严格相等时,才认为两个key相等。允许使用key,但不保证键值对之间的顺序。

WeakHashMap###

  与HashMap基本相同,HashMap的key保留对象的强引用即只要HashMap对象不被销毁,其对象所有key所引用的对象不会被垃圾回收,HashMap也不会自动删除这些key所对应的键值对对象。但WeakHashMap的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被回收。WeakHashMap中的每个key对象保存了实际对象的弱引用,当回收了该key所对应的实际对象后,WeakHashMap会自动删除该key所对应的键值对
  ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的

Map集合遍历###

import java.util.*;
public class MapI{
	public static void main(String[] args){
		Map<Integer, String> m = new HashMap<Integer, String>();
		m.put(1,"String1");
		m.put(2,"String2");
		m.put(3,"String3");
		m.put(4,"String4");
		
		//Map.Entry<Integer, String> me = m.entrySet();
		Iterator<Map.Entry<Integer, String>> iterator = m.entrySet().iterator();
		while(iterator.hasNext()){
			Map.Entry<Integer,String> entry = iterator.next();
			System.out.println(entry.getKey() + " --- " + entry.getValue());
		}
		
		for(Map.Entry<Integer,String> entry: m.entrySet()){
			System.out.println(entry.getKey() + " --- " + entry.getValue());
		}
	}
}

HashSet###

  散列码是由对象导出的一个整数值。在Object中有一个hashCode方法来得到散列码。基本上,每一个对象的默认散列码,其值就是对象的内存地址。但也有一些对象的散列码不同,比如String对象,它的散列码就是对内容的计算结果:

	public int hashCode() {
        int h = hash; //hash默认值0
        if (h == 0 && value.length > 0) { //value是字符数组
            char val[] = value;

            for (int i = 0; i < value.length; i++) { //遍历字符数组
                h = 31 * h + val[i];   //31乘以现有hash值加上数组元素,String通过内容计算的
            }
            hash = h; 
        }
        return h; //返回hash值
    }

  HashSet的添加机制(默认大小:16,每次增加一倍):Java默认的散列单元大小全部都是2的次幂,初始值为16(2的4次幂)。加入链表的75%有数据的时候,则认为加载因子达到默认的0.75,HashSet开始重新散列,也就是将原来的散列结构全部抛弃,重新开辟一个散列单元为32(2的5次幂)的散列结果,并重新计算各个数据的存储位置。
  根据数据的散列码和散列表的数组大小计算除余后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可。
  查找的代价就是在链表中,但是真正一条链表中的数据很少,有的甚至没有。几乎没有什么迭代的代表可言,所以散列表的查找效率教黎在散列单元所指向链表中的数据要少。
  HashSet不能重复存储equals相同的数据。原因就是equals相同,数据的散列码也就相同。大量相同的数据存放在同一个散列单元所指向的链表中,造成严重的散列冲突,对查找效率灾难性的

LinkedHashSet
  以哈希表和链表实现的set集合,链表定义了迭代顺序,即我们插入元素的顺序。如果一个元素被重复插入到集合中并不会改变集合的插入顺序
  LinkedHashSet在迭代访问Set全部元素时,性能比HashSet好。但是在插入的时候稍微逊色于HashSet。

TreeSet###

比较器比较

		Set<Person> s = new TreeSet<Person>(
				new Comparator<Person>(){
					public int compare(Person p1, Person p2) {
						int num = p2.getAge() - p1.getAge();
						return num;
					}

				}
		);

  自然排序:通过实现Comparable接口
  TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
  TreeSet如果传入Comparator, 就优先按照Comparator

ArrayList 初始值10,扩容倍数1.5 新的容量是1.5+1
ArrayDeque 初始值8,扩容倍数2
vector 初始值10,扩容倍数2
BitSet 初始值64,扩容倍数2
HashMap 初始值16,扩容倍数2
HashSet/TreeSet 同HashSet(基于HashMap实现,value为空Object)
Hashtable 初始值11,扩容倍数2
WeakHashMap 同HashMap
PriorityQueue 初始值11,两倍太小的话再增加50%
注意:HashSet,HashMap,Hashtable的加载因子都是0.75,即容量达到0.75就扩容。

原文地址:https://www.cnblogs.com/changzuidaerguai/p/8481765.html