集合体系
集合是可变长的。
Collection:代表单列集合,每个元素包含一个值。
Map:代表双列集合,每个元素包含两个值。
Collection集合
- List系列集合:添加的元素是有序、可重复、有索引。
- ArrayList、 LinekdList:有序、可重复、有索引。
- Set系列集合:添加的元素是无序、不重复、无索引。
- HashSet: 无序、不重复、无索引;
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:按照大小默认升序排序、不重复、无索引。
常用功能与遍历
Collection常用功能
方法名 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
遍历方式
迭代器遍历
迭代器遍历,集合遍历的专用方式。
Collection<String> names = new ArrayList<>();Iterator<String> it = names.iterator();
it.next();while(it.hasNext()){it.next();
}
迭代器初始位置为容器位置为0处,调用next()
取出当前位置数据并移到下个位置。调用hasNext()
判断当前位置有无数据,并移到下个位置。
增强for循环遍历
既可以遍历数组,也可以遍历集合。本质是迭代器的简化写法。快捷写法是.for
。
for(元素的数据类型 变量名:数组或者集合){
}Collection<String> c = new ArrayListe();
// c.for
for(String s : c) {
System.out.println (s);
}
Lambda表达式遍历
default void forEach(Consumer<? super T> action)
Collection<String> lists = new ArrayList<>();
lists. forEach(new Consumer<String>() {@Overridepublic void accept (String s) {System.out.println(s);}
});
// 使用lambda表达式
lists.forEach(s -> {System.out.println(s);});
// 简化
lists.forEach(s -> System.out.println(s));
// 方法引用简化(没必要到这一步)
lists.forEach(System.out::println);
三种遍历方式的区别
认识并发修改异常问题
遍历集合的同时又存在增删集合元素的行为时可能出现业务异常,这种现象被称之为并发修改异常问题。
解决方案1:删除操作后i--
解决方案2:倒着遍历并删除(前提是支持索引)
三种遍历方式能否解决并发修改异常
遍历方案1:迭代器也存在并发修改异常问题会直接抛异常,不要用集合的remove()
去删,而是用迭代器的remove()
去删。
遍历方案2和3:增强for和Lambda都不行。
List
List的特有方法
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
ArrayList的底层原理
底层基于数组存储数据。
数组的特点:
- 查询速度快(注意:是根据索引查询数据快):按照索引查询任意数据耗时相同
- 增删数据效率低:可能要把后面的数据前移;可能要复制数组到更大的数组中。
- 第一次扩容为10,第二次扩容15。扩容到原来的1.5倍
LinkedList的底层原理
底层基于双向链表存储数据。
链表的数据是一个一个独立结点组成,在内存中不连续,每个结点包含数据值和下一个结点的地址。
链表的特点:
- 查询慢,无论查询那个数据都要从头开始。
- 链表增删相对较快。
双向链表的特点: - 对首尾元素进行增删改查的速度是极快的。
- 可以设计队列和栈。
针对这些特点,LinkedList新增了很多首尾操作的特有方法。
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
独有功能不要用多态。
Set
Set要用到的常用方法,基本上就是Collection提供的!!自己几乎没有緩外新增一些常用功能!
特点:无序,不重复,无索引
HashSet:无序、不重复、无索引。
LinkedHashSet: 有序、不重复、无索引。
TreeSet:排序(大小升序排)、不重复、无索引。
HashSet的底层原理
基于哈希表存储数据的。
哈希值:int类型的随机值,Java中每个对象都有一个Hash值。可以调用Object类提供的hashCode方法,返回该对象自己的哈希值。
不同的对象的哈希值大概率不相等,但有可能会出现哈希碰撞。
JDK8之前,哈希表=数组+链表。
JDK8开始,哈希表=数组+链表+红黑树。
哈希表是一种增删改查数据,性能都很好的数据结构。
底层原理
- HashSet创建一个默认长度16的数组,默认加载因子为0.75,数组名为table
- 使用元素的哈希值对数组的长度做运算,计算出应存入的位置。
- 判断当前位置是否为null,如果是null直接存入。
- 如果不为null,表示有元素,先调用equals方法比较。不相同,形成链表。
- JDK8之前,新元素占老元素位置,老元素挂下面;JDK8开始,新元素挂老元素下面。
扩容机制
- 整个哈希表长度超过长度×加载因子,就进行扩容以维护性能。扩容到原来的两倍,第一次32.
- JDK8之后,当链表长度超过8,且数组长度≥64,自动将链表转换成红黑树,提高检索性能。
缺点
- 占内存。
- 不能重复。
- 没有索引。
如果希望Set集合认为2个内容相同的对象是重复的应该怎么办?
- 重写对象的hashCode和equals方法。
LinkedHashSet集合的底层原理
有序,无重复,无索引。
底层依然基于哈希表:数组+链表+红黑树 实现的。
但是,它的每个元素都额外多了一个双链表的机制记录它前后元素的位置。
TreeSet集合的底层原理
一定要排序,无重复,无索引。
底层基于红黑树实现。给一个数据就要排序.
注意:
- 对于数值类型:Integer,Double,默认按照数值本身的大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
TreeSet的自定义排序
对于自定义对象,如何解决排序?
- 对象类实现一个comparable比较接口,重写
compareTo
方法,指定大小比较规则。 public TreeSet (Comparator c)
集合自带比较器Comparator对象,指定比较规则。- 规定:如果左边大于右边 请返回正整数。
- 按照规则的比较数据进行重复判定,比如两个学生的age是相等的,treeset会去掉一个。
Map集合
需要存储一一对应的数据时,就可以考虑使用Map集合来做。
Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值。
- HashMap(由键决定特点):无序、不重复、无索引;(用的最多)。键值对可以为null。
- LinkedHashMap(由键决定特点):由键决定的特点:有序、不重复、无索引。
- TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
Map<String,Integer> map = new HashMap<>(); //经典代码
map.put("111",100);
Map集合的常用功能
方法名称 | 说明 |
---|---|
public V put(K key,V value) | 添加元素 |
public int size() | 获取集合的大小 |
public void clear() | 清空集合 |
public boolean isEmpty() | 判断集合是否为空,为空返回true,反之为false |
public V get(Object key) | 根据键获取对应值 |
public V remove(Object key) | 根据键删除整个元素 |
public boolean containsKey(Object key) | 判断是否包含某个键 |
public boolean containsValue(Object value) | 判断是否包含某个值 |
public Set |
获取全部键的集合 |
public Collection |
获取Map集合的全部值 |
Map集合的遍历方式
键找值遍历
先获取Map集合全部的键,再通过遍历键来找值。
方法名称 | 说明 |
---|---|
public Set |
获取所有键的集合 |
public V get(Object key) | 根据键获取其对应的值 |
键值对整体遍历
把键值对看成一个整体Set进行遍历。
Map提供的方法 | 说明 |
---|---|
Set<Map.Entry<K, V>> entrySet() | 获取所有""键值对""的集合 |
Map.Entry提供的方法 | 说明 |
---|---|
K getKey() | 获取键 |
V getValue() | 获取值 |
for (Map. Entry<String, Double> entry : entries) {String key = entry.getKey() ;double value = entry.getValue():System.out.println(key + "====>" + value);
Lambda表达式遍历
JDK 1.8开始之后的新技术(非常的简单)。
方法名称 | 说明 |
---|---|
default void forEach(BiConsumer<? super K, ? super V> action) | 结合lambda遍历Map集合 |
map. forEach((k, v) -> {System.out.println(k + "—----" + v);
});