Java集合

视频教程地址:
https://www.bilibili.com/video/BV1kt411v7gi/?p=1

List

List是有序、可重复的容器
有序:List中每个元素都有索引标记
可以根据元素的索引标记访问元素
可重复:List允许加入重复的元素
更准确的将,List允许慢速e1.equals(e2)的元素重复加入容器
List接口常用的实现类:
ArrayList

底层由数组实现
特点:查询效率高,增删效率低,线程不安全
一般使用它

LinkedList

LinkedList底层用双向链表实现存储。
特点:查询效率低,增删效率高,线程不安全
双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。
所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。
每个节点都应该有3部分内容:

	class Node {
		Node previous; //前一个节点
		Objedct element;//本节点保存的数据
		Node next;//后一个节点
	}

Vector

Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”

建议:
如何选用ArrayList、LinkedList、Vector?
需要线程安全时,用Vector
不存在线程安全问题,查找较多,用ArrayList
不存在线程安全问题,增删元素较多,用LinkedList

ArrayList<>()

常用方法

增删改查方法

List list = new ArrayList<>();
list.add(“1”);//添加元素
list.add(0,“2”);//指定位置添加元素
list.set(0,“高老二”);//设置元素
res = list.get(0);//获取指定位置元素
int i = list.indexOf(“1”);//返回元素的索引,如果list中不存在该元素,返回-1
i = list.lastIndexOf(“1”);//返回元素最后一次出现的位置
代码实例

    public static void test04() {
        List<String> list = new ArrayList<>();
        list.add("1");//添加元素
        list.add(0,"2");//指定位置添加元素
        String res = list.get(0);
        System.out.println(res);
        list.set(0,"高老二");//设置元素
        res = list.get(0);//获取指定位置元素
        System.out.println(res);
        int i = list.indexOf("1");//返回元素的索引,如果list中不存在该元素,返回-1
        i = list.lastIndexOf("1");//返回元素最后一次出现的位置,
        System.out.println(i);
    }

集合运算方法

list01: [aa, bb, cc]
list02: [aa, dd, ee]
list01.addAll(list02);//把list02中的元素添加到list01中 list01: [aa, bb, cc, aa, dd, ee]
list01.removeAll(list02);//去除list01中与list02相同的元素 list01: [bb, cc]
list01.retainAll(list02);//去除两个集合共有的元素
list01: [aa]
list01.containsAll(list02)//判断list01中是否包含list02中所有元素

代码实例:

package cn.sxt.collection;

//测试Collection接口中的方法
   public static void test02() {
        List<String> list01 = new ArrayList<>();
        list01.add("aa");
        list01.add("bb");
        list01.add("cc");
    
        List<String> list02 = new ArrayList<>();
        list02.add("aa");
        list02.add("dd");
        list02.add("ee");
    
        System.out.println("list01: "+list01);
        System.out.println("list02: "+list02);
//        list01.addAll(list02);//把list02中的元素添加到list01中  list01: [aa, bb, cc, aa, dd, ee]
//        list01.removeAll(list02);//去除list01中与list02相同的元素    list01: [bb, cc]
        list01.retainAll(list02);//去除两个集合共有的元素      list01: [aa]
        System.out.println(list01.containsAll(list02));//判断list01中是否包含list02中所有元素
        System.out.println("list01: "+list01);
    }
}

Map

Map(接口)用来存储“键-值”对 (key-value)
Map类存储的“键-值”对通过“键”来表示,所以“键”对象不能重复
Map的实现类有HashMap、TreeMap、HashTable、Properties等
常用方法
在这里插入图片描述

HashMap

常用方法

m1.put(key, value);//存对象
//Map中key不能重复
//是否重复根据equals方法比较
//如果重复,新的覆盖旧的
m1.get(key);//根据key取对象
m1.remove(key);//删除键值对
m1.size();//获取键值对的数量
boolean b = m1.isEmpty();//判断map是否为空
m1.clear();//清空map对象所有键值对
m1.putAll(map);//将map中的所有键值对加入m1中
m1.containsKey(key));//是否包含指定的key
m1.containsValue(value);//是否包含指定的value

HashMap底层实现采用了哈希表,这是一种非常重要的数据结构
哈希表的基本结构就是“数组+链表”

HashMap存储键值对过程

Entry对象,为单向链表结构,属性有key,value,hash(key的hash值),next(链表结构中指向下一个Entry对象)
jdk1.8把Entry[]改名为Node[]
HashMap中维护一个Node[]数组:Node[] table,默认初始大小为16
Entry[] table是HashMap的核心数组结构,也叫做位桶数组
哈希表的基本结构就是 数组+链表

在这里插入图片描述
在这里插入图片描述
put一对key-value->
key-value对象包装为一个Entry对象
根据key得到key的hashcode,key.hashcode()->
用散列算法,根据hashcode得到一个hash值(一个int数值,范围在0-table数组大小-1,例如,12)->
把Entry对象存入相应下标的Entry[]数组中(例如,存入Entry[12])->
如果下次根据key2计算得到的数组下标与之前相同,即该数组下标后面已经存放了一个Entry对象,则存到第一个key-value对象后面
因此根据key.hashcode()计算得到的Entry[]数组下标index越分散越好(散列算法),因此经常开发新的散列算法

i 一种极端简单低下算法:hashcode/hashcode
得到的hash总是1,意味着所有key-value对象都会存储到数组索引1的位置,每一个存储对象都会发生hash冲突,HashMap退化成一个链表
ii 一种简单常用算法(相除取余法):hash = hashcode%数组长度 效率低下
iii hash = hashcode&(数组长度-1) (按位与,要求数组长度为2的n次方,可以实现均匀的散列算法)
jdk1.8以后,当链表长度大于8时,链表转换为红黑树,大大提高查找的效率

HashMap取数据过程 get(key)

获得key的hashcode,通过hash()散列算法得到hash值,进而定位到数组的位置
在链表上挨个比较key对象,调用equals方法,将key对象和链表上所有节点的key对象进行比较,直到遇到返回true的节点对象为止
返回equals()为true的节点对象的value对象

java中规定,两个内容相同(equals()为true)的对象必须具有相等的hashcode。因为如果equals()为true而两个对象的hashcode不同,那在整个存储过程中就发生了悖论

扩容问题

HashMap的位桶数组,初试大小为16
实际使用时,大小是可变的
如果位桶数组中的元素达到(0.75*数组.length),就重新调整数组大小变为原来2倍大小
扩容本质是定义新的更大的数组,并将旧数组中的内容挨个拷贝到新数组中
扩容很耗时
JDK8将链表在大于8的情况下变为红黑二叉树,提高查找效率

HashMap与HashTable的区别

HashMap线程不安全, 效率高,允许key或value为null
HashTable线程安全,效率低,,不允许key或value为null

TreeMap

TreeMap是红黑二叉树的典型实现
TreeMap和HashMap实现了同样的接口Map,因此,用法对于调用者来说没有区别
HashMap效率高于TreeMap
在需要排序的Map时才选用TreeMap
如果采用自定义的类作为HashMap的key, 自定义的类需要实现Comparable接口, 覆写compareTo()方法

Set

无序,不可重复

无序:元素没有索引,只能遍历查找

不可重复:不允许加入重复的元素,更确切的讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入
常用子类:HashSet,TreeSet
在这里插入图片描述

HashSet

HashSet是采用哈希算法实现,底层实际是用HashMap实现的
HashSet本质就是一个简化版的HashMap,因此查询和增删效率都比较高
set.add(E element)方法实际添加的是HashMap中map.put(E element, new Object())
(把HashSet中的元素添加到HashMap中的key中,value用new Object()填充)

    public HashSet() {
        map = new HashMap<>();
    }
	...
    private static final Object PRESENT = new Object();
    ...
        public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

TreeSet

TreeSet底层实际是使用TreeMap实现的, 通过key来存储Set的元素
TreeSet内部需要对存储的元素进行排序
因此, 对应的类需要实现Cmparable接口

Collection工具类

java.util.Collections提供了对Set, List, Map进行排序、填充、查找元素的辅助方法
void sort(List)//对List容器内的元素按照升序排序
void shuffle(List)//度List容器内的元素进行随机排列
void reverse(List)//对List容器内的元素进行逆序排列
void fill(List, Object)//用一个特定的对象重写整个List容器
int binarySearch(List, Object)//对于顺序的List容器,采用这般查找的方法查找特定对象

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值