集合总结、注意事项

集合

1. Java集合简介

  1. 特点:
    • 实现了接口和实现类的分离
    • 支持泛型,一个集合中只能存入一种类型的元素
    • 通过迭代器(Iterator)访问集合,而无需知道内部的存储方式
  2. 包括:Collection下的List、Set接口,Map接口
  3. 遗留类:Hashtable:线程安全的Map;Vector:线程安全的List;Stack:基于Vector的栈;不应再使用

2. List

  1. List和数组几乎一样:有序、使用索引
  2. 不同的是:数组的添加、删除(中间的)元素时,需要挪动元素,很麻烦,并且数组容量受限;
    而List的ArrayList实现类就很方便,它把添加删除给封装了起来;在元素满时还会自动扩容(扩容1.5倍,复制原数组的数据,再修改原数组的引用);
  3. 接口方法:
  • 在末尾添加一个元素:boolean add(E e)
  • 在指定索引添加一个元素:boolean add(int index, E e)
  • 删除指定索引的元素:E remove(int index)
  • 删除某个元素:boolean remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size()
  1. LinkedList通过链表也实现了List接口;LinkedList相比于ArrayList:获取指定元素更慢、中间的插入删除更快、内存占用更大

  2. List接口特点:

    • 允许添加重复元素
    • 允许添加null元素
  3. 创建:

    • new ArrayList()/LinkedList()
    • JDK9新增创建不可变集合的静态方法:List.of(E…),创建后不能添加删除元素,不能有null
  4. 遍历List:

    • for + get(int index):get()方法只对ArrayList高效,对于LinkedList速度很慢(链表需要从头开始遍历找)
    • 迭代器Iterator:由List实例调用iterator()方法创建Iterator对象;不同的List实例有不同的Iterator对象;而这个Iterator对象的子类对象有不同List的最高效的访问方式;
      通过Iterator对象的boolean hasNext()方法来判断有无下一个元素,E next()方法来返回下一个元素;
    • for each:就是简化的Iterator方式
      编译器会把foreach转为迭代器
	 for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
	            String s = it.next();
	            System.out.println(s);
	}

     for (String s : list) {
         System.out.println(s);
     }
  1. 集合=>数组:Integer[] array = list.toArray(new Integer[list.size()]);
    数组=>集合:Arrays.asList(T…)
    jdk9: List list = List.of(array);

3. 编写equals方法

  1. 为什么?
    List中如何判断两个集合元素是否相等,如boolean contains(Object o)int indexOf(Object o)方法?
    使用equals()方法判断,若集合元素类型没有重写该方法,则使用==来判断,对于引用类型来说,是判断内容相等,而不是引用相等,所以需要重写equals();
  2. equals(Object o)方法接收的是Object,所以使用instanceof判断Object是否指向当前实例,是就向下转型,否则返回false;
    基本类型使用==,引用类型使用Objects.equals(this.name , p.name );

直接使用引用类型属性的equals方法需要先判空,并且都为null时认为相等;
所以使用Objects.equals()方法比较方便

  1. 如果不使用类似contains()、indexOf()这些内部需要判断元素相等的方法,那么放入集合的元素就可以不实现equals()方法;

4. Map

  1. 使用List进行查找时平均扫描一半元素才可以,而使用Map可以根据key高效查找

  2. Map<K,V>是键值映射表,使用V put(K k,V v)就存入一个键值对,k存在返回k原来的对应值,不存在返回null,再通过 V get(K k)就可以获取值,不存在返回null;

  3. 遍历Map:

    • foreach遍历map.keySet(),得到key的集合;
    • foreach遍历map.entrySet(),得到的就是每个键值对了,entrySet()中每一个是Map.Entry<键,值>内部类实例
  4. 没有顺序

5. 编写equals和hashCode

  1. HashMap可以根据key直接拿到value:因为内部用一个大数组存储所有的value,并根据k直接计算出value所在的数组索引
  2. key不能重复,使用key查找时,两个key应该内容相同。Map中对key作比较通过equals实现,和List的查找类似,map的key对象必须重写equals方法
  3. 如何根据key计算出value的索引?并且相同的key(equals()为true)必须计算出相同的索引
    通过key的hashCode()方法得到一个整数再经过hash函数得到value的索引,进而返回value;
    所以还要重写key的hashCode()函数,hashCode()的规范:
  • 两对象相等(equals为true),则hashCode必须相等;(必须满足)
  • 两对象不相等,hashCode尽量不相等;(尽量满足,保证查询效率)
  1. 和equals()一样,需要对字段判空,所以使用Objects.hash(字段1。。。)

equals()中用到的字段,必须也用在hashCode()中;
equals()中没有用到的字段,不能用在hashCode()中;

  1. hashMap初始化数组大小只有16,而key的hashCode值很大,& (容量-1),这样就可以把索引确定到(0,容量-1),保证不会超数组范围
  2. 如果超出数组容量,HashMap内部字段扩容(2倍),再重新根据hashCode值计算新的索引:&(新容量-1);

扩容会导致key-value对被重新分布,所以创建HashMap时最好指定容量new HashMap<>(10000);

这个容量虽然可以随意设定,但是HashMap内部的数组长度总是:2n,是为了计算索引时更方便(容量为2n,可以使用&代替%,计算机&速度更快),比10000大的最小的是214=16384;

  1. 两个key的hashCode()尽量不相等,但如果相等了,怎么办?(哈希冲突)
    在这里插入图片描述
    这个数组存储的不是单纯的value,而是一个List链表,链表中存的是键值对

Person p = map.get(“a”);

查找时,根据a找到数组索引的那个List链表,之后再遍历这个List,找到key为a的那个entry,然后再返回这个value;

冲突概率越大,查找效率越低,所以尽量满足hashCode不相等

6. Set

  1. 只存储不重复的key,就用Set
  2. 方法
  • 将元素添加进Set:boolean add(E e)
  • 将元素从Set删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)
  1. 经常使用Set去除重复元素;
  2. Set的元素和Map的key类似,也要重写equals()、hashCode()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LJFCZH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值