Java基础-集合框架

框架图

在这里插入图片描述

Collection

  • Collection:采用线性列表的方式存储,长度可动态改变。
    • List:记录元素插入顺序,允许存在重复元素。
      • ArrayList:使用动态数组方式存储元素,支持随机读取,适合做查询操作,是线程不安全的。
      • Vector:是ArrayList线程安全的实现。因为是线程安全的,所以性能较ArrayList稍差。
      • LinkedList:使用双向链表的方式存储元素,适合做增删操作,是线程不安全的。
    • Set:不记录元素插入顺序,不允许存在重复元素。
      • HashSet:容器中的元素是无序的。
      • TreeSet:容器中的元素是按照自然排序进行排序的,排序规则可自定义。
    • Queue:提供了一种先进先出的操作方式,只允许在容器的前端进行删除,在后端进行插入。

List

  • ArryaList:使用动态数组实现,其容量随着元素的增加可以自动扩张(1+50%),特点是查询效率高。

    ArrayList<String> al = new ArrayList<>();
    		al.add("张三");
    		al.add("李四");
    		al.add("王五");
    		System.out.println(al);
    

    结果:[张三, 李四, 王五]

    • 读取过程:动态数组的物理存储位置是连续的,知道ArrayList第一个元素的物理存储地址和每个元素的大小,即可通过下标算出任意元素的物理地址,所以查询效率高。
      例如:张三的地址为0000001,下标为1,每个对象占两个字节,则下标为3的对象的物理起始地址为:
      (3-1)x 2 + 0000001 = 0000005
      在这里插入图片描述
    • 增删过程:删除ArrayList中间的元素后,被删除元素后的所有元素要向前移动,所以删除元素时效率较低。
      在这里插入图片描述
  • Vector:特性与ArrayList一直,只是在线程安全方面进行了处理,因此它是同步的,自动扩容容量(1+1)。在存在大量元素时,因为是线程安全的,所有性能逊色于ArrayList

    Vector<String> al = new Vector<>();
    		al.add("张三");
    		al.add("李四");
    		al.add("王五");
    		System.out.println(al);
    

    结果:[张三, 李四, 王五]

  • LinkedList:基于双向链表实现的,所以对元素的增删支持好,查询方面不如ArrayList

    LinkedList<String> al = new LinkedList<>();
    		al.add("张三");
    		al.add("李四");
    		al.add("王五");
    		System.out.println(al);
    

    结果:[张三, 李四, 王五]

    • 读取过程:双向链表的物理存储位置是不连续的,每个元素保存着下一个元素和上一个元素的物理位置。查找下标为3的元素时,需要一次查询每一个元素用以获取下一个元素的物理地址,所以查询性能低于ArrayList。
      在这里插入图片描述
    • 增删过程:删除元素时,其余元素位置不需要移动,只需改变删除元素前后元素的地址指向即可。
      在这里插入图片描述

Set

  • HashSet
    HashSet是基于Hash算法实现的,其性能比TreeSet好,特点是增删元素较快。

    HashSet<String> hs = new HashSet<>();
    		hs.add("张三");
    		hs.add("张三");
    		hs.add("李四");
    		hs.add("李四");
    		hs.add("王五");
    		hs.add("王五");
    		System.out.println(hs);
    

    输出结果:[李四, 张三, 王五]

    TIPS:HashSet每次添加对象时,会使用equals(),根据散列码来判断是否重复,可以通过Object.hashCode()获取对象的散列码。

  • TreeSet
    TreeSet中的元素除了没有顺序和不能重复外,还会进行自然排序,或自定义排序。

    TreeSet<String> ts = new TreeSet<>();
    		ts.add("张三");
    		ts.add("张三");
    		ts.add("李四");
    		ts.add("李四");
    		ts.add("王五");
    		ts.add("王五");
    		System.out.println(ts);
    

    输出结果:[张三, 李四, 王五]

  • TreeSet自定义排序规则

    /*
    * 通过实现Comparable接口,并重写compareTo方法来实现自定义排序规则。
    */
    public class Student implements Comparable<Student> {
    	
    	
    	private String name;
    	
    	private Integer age;
    	
    	public Student(String name,Integer age) {
    		this.name = name;
    		this.age = age;
    	}
    	
    
    
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    
    
    
    	@Override
    	public int compareTo(Student o) {
    		if(this.age < o.age) {
    			// 负整数 当前对象小于传入的对象
    			return -1;
    		}else if(this.age > o.age) {
    			// 正整数 当前对象大于传入的对象
    			return 1;
    		}else {
    			// 相等 元素不可加入
    			return 0;
    		}
    	}
    
    }
    
    
    public static void main(String[] args) {
    		Student one = new Student("张三", 1);
    		Student two = new Student("李四", 3);
    		Student three = new Student("王五", 2);
    		Student four = new Student("刘六", 2);
    		TreeSet<Student> ts = new TreeSet<>();
    		ts.add(one);
    		ts.add(two);
    		ts.add(three);
    		ts.add(four);
    		ts.add(four);
    		System.out.println(ts);
    	}
    

    结果:[Student [name=张三, age=1], Student [name=王五, age=2], Student [name=李四, age=3]]

Queue

提供了一种先进先出的操作方式,只允许在容器的前端进行删除,在后端进行插入(类似于现实中的排队),不可对中间元素进行操作。是对增删操作进行限制的List,二者实现方式也不同。
在这里插入图片描述

Queue<String> qu = new LinkedList<>();
		qu.add("1");
		qu.add("2");
		qu.add("3");
		qu.add("1");
		System.out.println(qu);
		System.out.println(qu.poll());
		System.out.println(qu);

输出结果:
[1, 2, 3, 1]
1
[2, 3, 1]

Map

Map接口及其实现类采用键值对的方式存储,长度可动态改变。

  • HashMap:基于散列表的Map接口实现类,是线程不安全的。
  • HashTable:实现方式与HashMap一致,但是线程安全的。
  • TreeMap:根据红黑树算法实现的Map接口,支持自然排序,是线程不安全的。

HashMap

基于散列表的Map接口实现类,是线程不安全的。

假设张三的hashCode为17,则其table数组位置为:17%16=1,若存在多个余数为1的情况,则链接在前一个元素后。
在这里插入图片描述
HashMap中顶一个一些成员变量:

// hashMap数组的初始容量 16(大小必须是2的幂次方)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 负载因子 0.75f;(元素达到16*0.75个时,会自动扩容,负载因子大于1时,永不扩容)
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 树形化阈值 8(链表元素个数达到8个时,自动转换为红黑色)
static final int TREEIFY_THRESHOLD = 8;
// 解除树形化阈值 6(当链表的节点个数小于等于这个值时,会将红黑树转换成普通的链表。)
static final int UNTREEIFY_THRESHOLD = 6;
// 树形化的另一条件 Map数组的长度阈值 64(树形化阈值的第二条件。当数组的长度小于这个值时,就算树形化阈达标,链表也不会转化为红黑树,而是优先扩容数组resize()。)
static final int MIN_TREEIFY_CAPACITY = 64
// 这个就是hashMap的内部数组了,而Node则是链表节点对象。
transient Node<K,V>[] table;
// 数组扩容阈值。(即:HashMap数组总容量 * 加载因子。当前容量大于或等于该值时会执行扩容)
int threshold;
// 扩容的容量为当前HashMp的两倍
resize();

使用示例:

HashMap<Integer, String> hm = new HashMap<>();
		hm.put(1, "中国");
		hm.put(2, "江苏");
		hm.put(3, "南京");
		System.out.println(hm);

结果:{1=中国, 2=江苏, 3=南京}

HashTable

实现方式与HashMap一致,但是线程安全的,因此相对而言HashMap性能会高一些。
HashMap和Hashtable的区别:

  • 线程安全
    两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全。
    Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合。

  • HashMap可以使用null作为key,而Hashtable则不允许null作为key
    HashMap以null作为key时,总是存储在table数组的第一个节点上。

  • 继承结构
    HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类。

  • 初始容量与扩容
    HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
    HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1。

  • 两者计算hash的方法不同

    • Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取余。

      int hash = key.hashCode();
      int index = (hash & 0x7FFFFFFF) % tab.length;
      
    • hashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取余。

      int hash = hash(key.hashCode());
      int i = indexFor(hash, table.length);
      
      static int hash(int h) {
              // This function ensures that hashCodes that differ only by
              // constant multiples at each bit position have a bounded
              // number of collisions (approximately 8 at default load factor).
              h ^= (h >>> 20) ^ (h >>> 12);
              return h ^ (h >>> 7) ^ (h >>> 4);
          }
       
       static int indexFor(int h, int length) {
              return h & (length-1);
      

TreeMap

是根据红黑树(平衡二叉树)算法实现的,并支持自然排序。

红黑树规则如下:
1、每个节点都只能是红色或者黑色
2、根节点是黑色
3、每个叶节点(NIL节点,空节点)是黑色的。
4、如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值