Java集合(Collection+Map)
- 为什么要使用集合?
- 泛型 <>
- 集合框架
- 单列集合Collection
- Collection遍历方式
- List:有序、可重复、有索引
- ArrayList
- LinkedList
- Vector(已经淘汰,不会再用)
- Set:无序、不重复、无索引
- HashSet
- LinkedHashSet
- TreeSet
- 双列集合Map
- HashMap
- LinkedHashMap
- HashTable(后续补充)
- Properties
- TreeMap
- Collections 集合工具类
来自黑马,把视频截图保存,主要为了自己方便查找。
为什么要使用集合?
当有很多数据需要存储时,会想到用数组,而数组需要在定义时指定长度,而我们希望有一个可变长度的容器——集合。
集合可以自动扩容,提高了数据存储的灵活性,Java 集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据。
泛型 <>
集合框架
红色表示接口,蓝色表示实现类:
键和值之间一一对应。键值对也成为键值对对象,或者是Entry对象。
单列集合Collection
在Java里面,字符串已经重写好了equals方法。
Collection遍历方式
以前的普通的for循环的遍历方式适用于有索引的list系列,而没有索引的set系列不能用。
1. 迭代器遍历
2. 增强for遍历
3. lambda表达式遍历
一行搞定:
List:有序、可重复、有索引
- 添加元素:添加完之后,原来索引上的元素会依次后移。
- 删除元素:下面第一个会删除1索引,第二个装箱了,会删除“1”
独有的遍历方式: 继承了Collection的遍历方式。
- 迭代器
- 增强for
- Lambda表达式
- 普通for循环(因为List集合存在索引)
- 列表迭代器遍历
总结:
ArrayList
输出是 []
import java.util.*;
public class Main {
public static void main(String[] args) {
//1.创建一个集合
ArrayList<String> list = new ArrayList<>();
System.out.println(list); //[]
//2.添加元素: 对于add方法,在ArrayList里面,不管添加什么,都会返回true
// boolean result = list.add("aaa");
// System.out.println(result); //true
// System.out.println(list); //[aaa]
// 所以一般直接添加元素就行,不用管返回值
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println(list); //[aaa, bbb, ccc]
//3.删除元素
list.remove("aaa");
System.out.println(list); //[bbb, ccc]
String str = list.remove(0); //这里也可以不接收返回值,直接list.remove(0)
System.out.println(str); //bbb
System.out.println(list); //[ccc]
//4.修改元素
String str1 = list.set(0, "ddd");
System.out.println(str1); //ccc,返回被覆盖的元素
System.out.println(list); //[ddd],结果就是覆盖以后的
//5.查询元素
String str2 = list.get(0);
System.out.println(str2); //ddd,获取单个元素
//遍历
list.add("eee");
list.add("fff");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); //依次输出ddd eee fff
}
System.out.println(list); //[ddd, eee, fff],直接打印就是集合
}
}
LinkedList
我们在项目中一般是不会使用到 LinkedList 的,需要用到 LinkedList 的场景几乎都可以使用 ArrayList 来代替,并且,性能通常会更好!就连 LinkedList 的作者约书亚 · 布洛克(Josh Bloch)自己都说从来不会使用 LinkedList 。
另外,不要下意识地认为 LinkedList 作为链表就最适合元素增删的场景。我在上面也说了,LinkedList 仅仅在头尾插入或者删除元素的时候时间复杂度近似 O(1),其他情况增删元素的时间复杂度都是 O(n) ,因为需要从头或尾开始寻找!
Vector(已经淘汰,不会再用)
Set:无序、不重复、无索引
set遍历:
首先添加元素,如果是第一次添加,返回True,第二次就不行了,返回False:
遍历:
HashSet
加载因子表示的是扩容时机,当数组里面的元素到了16*0.75=12个的时候,数组长度就加倍,变成了32。而当链表长度大于8而且数组长度大于等于64,就会自动编程红黑树:
了解了底层原理,就可以回答HashSet的问题:
如图所示,读取的顺序是图中的123456,但是存的顺序就不一定了。
问题2:HashSet为什么没有索引?
图中1索引对应的位置有好几个元素,不好区分,所以干脆无索引。
问题3:HashSet是利用什么机制保证数据去重的?
用HashCode方法和equals方法,所以如果集合中存储的是自定义对象(如Student),一定要根据对象的属性重写这两个方法,要不然就会比较地址值!
这里为什么会强调自定义对象要重写:如果存储的是基本数据类型的包装类,已经正确地重写了hashCode()和equals()方法。对于 Java 标准库中的许多类(如String),也已经合理地重写了hashCode()和equals()方法。
LinkedHashSet
添加进去的1和2,2和3,3和4……两两之间有双向链表,互相记录地址值。遍历的时候也是从1开始遍历。
TreeSet
如果字符串里面字母很多,那就从第一个字母开始比较:
“aaa” > “ab” > “aba” > “cd” > “qwer”
双列集合Map
①添加
②删除
Map的三种遍历方式:
HashMap
存储的时候只比较键的属性值,如果一样,就要覆盖,这一点和Set不同(Set是不存)! 这就是put里面覆盖的功能!
- HashMap底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法
LinkedHashMap
HashTable(后续补充)
Properties
TreeMap
这样直接输出已经是升序,如果要降序:
Collections 集合工具类