一,集合概述:
1.什么是集合类,为什么需要用到集合类
当我们需要保存一组一样(类型相同)的元素的时候,我们应该使用一个容器
来存储,数组就是这样一个容器。
数组的缺点:
数组一旦定义,长度将不能在变化
然而在实际开发中,存储的数据长度是不定的,是会变化的,在存储数据时,不同的使用场景,可以使用不同的数据结构,于是我们需要一些能够动态增长长度的容器来保存我们的数据,Java中对于各种数据结构的实现,就是我们用到的集合。
2.集合概述
Java的集合框架就是由许多接口,抽象类具体类组成的,都位于java。util包中
二,集合类体系结构
1.单列集合 :只能存在单个值
Collection接口定义了存取一组对象的方法,其子接口Set和List分别定义了不同的存储方法
List接口:可以存储重复元素
Set接口:不能存储重复元素
(1)List接口
List接口继承了Collection接口,有三个实现类
ArrayList:底层是数组实现,查询快,中间删除慢
LinkedList:底层是链表实现,查询慢,中间删除快
Vector:底层是数组实现,相较于ArrayList添加了同步锁,多线程场景下使用安全
(一)ArrayList
ArrayList是长度可变的数组,在内存中分配连续的空间。
遍历元素和随机访问元素的效率比较高
ArrayList数组集合,默认可以存储object类型
实际开发中,一般建议一个集合对象最好只存储一种单一类型
使用泛型语法,为集合指定一个类型
ArrayList中的常用方法:
ArrayList<String> arrayList = new ArrayList();
arrayList.add("a");//末尾添加元素
arrayList.add(3,"u");//向指定位置添加元素 arrayList.addAll(arrayList0);//把另一个集合数据添加进来
System.out.println(arrayList.remove(0));//根据索引删除对应的元素,并返回删除的元素System.out.println(arrayList.remove("b"));//根据内容删除第一次匹配的元素,
删除成功返回true,否则返回false
System.out.println(arrayList.isEmpty());//判断集合是否为空,是的话返回true System.out.println(arrayList.contains("c"));//判断是否包含指定
arrayList.set(1,"E");//替换指定位置的
System.out.println(arrayList.get(4));//获取指定位置的元素System.out.println(arrayList.indexOf("a"));//获取指定元素首次出现的位置System.out.println(arrayList.size());//返回集合中实际添加元素的个数
arrayList.clear();//清空元素
ArrayList<String> arrayList = new ArrayList<>();创建一个默认是10的数组
ArrayList<String> arrayList = new ArrayList<>(50);创建一个指定容量的数组 ArrayList<String> arrayList2 = new ArrayList<>(arrayList);
创建一个集合时,把另一个集合数据添加打此集合中
String[]strings = arrayList.toArray(new String[arrayList.size()]);//把集合对象转为数组对象
(二)LinkedList
LinkedList采用链表存储方式。插入、删除元素时效率比较高
LinkedList可以通过使用方法成为栈或队列
栈和队列
栈 :从头节点添加,从头节点删除
队列:排队 从尾添加 从头删除
LinledLis的常用方法:
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(4);//从尾节点添加元素
System.out.println(linkedList.get(3));//只能从头或者尾节点开始找,直到找到对应位置的元素
System.out.println(linkedList);
linkedList.set(2,40);//指定位置元素替换成指定元素;
System.out.println(linkedList.getFirst());//取头节点
System.out.println(linkedList.getLast());//取尾节点
System.out.println(linkedList.remove(2));//删除指定位置的元素
System.out.println(linkedList.removeFirst());//删除头节点
System.out.println(linkedList.removeLast());//删除尾节点
(三)List接口集合迭代
三种方式:
1.for循环
2.增强for循环的遍历
方式一:for循环
支持遍历时,删除数据
注意删除后,后面元素前移
for (int i = 0; i < arrayList.size(); i++) {
if (arrayList.get(i).equals("a") ){
arrayList.remove(i);
i--;
}
}
方式二:增强for循环
遍历时,不允许删除数据元素,如果删除元素会报错
for (String s : arrayList){
if (s.equals("a")){
arrayList.remove(s);
}
}
3.迭代器遍历(Iterator)
方式三 迭代器
Iterator<String> iterator = arrayList.listIterator();
while (iterator.hasNext()){
String item = iterator.next();
if (item.equals("a")){
iterator.remove();//调用迭代器中的remove方法
}
}
(2)Set接口
Set接口继承了Collection接口
Set中存储的元素是不重复的,Set中的元素是没有索引的
Set接口有两个实现类,且实现类的常用方法和迭代器与List接口的实现类的常用方法和迭代器一致
HashSet:HashSet类底层基于哈希表实现,元素位置是散列无序的
TreeSet:TreeSet类底层基于树形结构实现,元素进行指定的方式排列,存储的类型必须实现Comparable接口
(一)HashSet
HashSet 哈希(散列)本质上就是一个数组 算出余数是多少存在哈希表对应的位置,存储的元素是无序的
HashSet是如何实现值不重复原理
底层用到两个方法 HashCode() equal()
先调用hashCode(),根据内容算出一个哈希值(int类型),先用整数哈希值比较,哈希值相同,说明内容也就相同了
用哈希值判断是会存在风险的:内容不同,算出来的哈希值相同
所以哈希值相同的情况下,再调用equal()判断内容
结论:向HashSet中添加的数据类型中,必须调用hashCode()和equal()方法
向HashSet中存储我们自己定义的Student类型,如果没有重写hashCode(),默认会调用Object类中的hashCode()
Object类中 public native int hashCode();是一个native修饰的方法(是本地方法,是由操作系统提供的),读取的是对象的内存地址
所以只要是new出来的对象,对象地址都不同
现在的需要是,对象中的内容相同,就判定为重复内容
需要对Object类中的equal()和hashCode()进行重写,根据内容计算哈希值,判断内容是否相等
(二)TreeSet
TreeSet 树 存储元素是有序(排序的)
TreeSet是树形结构,第一个存入的数据是根,再继续添加元素是,进行比较,大的往右子节点存储,小的往左子节点存储
TreeSet 底层使用的是红黑树 可以自平衡 避免成为链表
向TreeSet集合中添加的类型必须实现Compare接口
2.双列集合:可以存储两个值
(1)Map接口:
概述:实现了Map接口的集合类就是双列集合
是一组键值对映射的
键不能重复,每个键只能映射到一个值
值可以重复
实现类
HashMap
TreeMap
(一)Map接口的常用方法
HashMap<String,String> map = new HashMap<>();
map.put("x","xx");
map.put("a","44");
map.put("u","uu");
map.put("k","kk");
map.put("a","aa");//添加元素,逗号前为键,逗号后为值,出现重复键时,用后出现的键映射的值覆盖先出现的键值
map.clear();//清空
map.remove("a");//通过key删除对应的映射
System.out.println(map.containsKey("a"));//判断是否包含指定的key
System.out.println(map.containsValue("xx"));//判断是否包含指定的value
System.out.println(map.isEmpty());//判断是否为空
System.out.println(map.size());//存了几个key字节
System.out.println(map.get("u"));//根据key获得对应的value
Collection<String> values = map.values();
System.out.println(values);
Set<String> s = map.keySet();
System.out.println(map);//输出是无序的
(二)HashMap
可以存储一个为null的键,key和value都可以为null
底层用到的数据结构: 哈希表(数组),链表,红黑树
(三)TreeMap
TreeMap 底层实现是红黑树,可以根据键进行排序
key值的类型必须实现 Comparable接口,重写Comparable方法
(四)HashTable
是无序的
也是哈希结构,方法都加了锁,在多线程场景下是安全的
不能存储为null的键和值
(五)Map集合的遍历
方式1:根据键找值
获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值
方式2:根据键值对对象获取键和值
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值
/*
方式一:先拿到所有的key,循环key,每次拿到一个key,然后在上map中去找值,(不推荐)
*/
Set<String> keyset = map.keySet();
for (String key:keyset){
System.out.println(key + ":" + map.get(key));
}
/*
方式二:
为了遍历方便,把所有的键值对,重新进行了一个提取,存入到一个entry类型集合中
*/
Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String,String> entry:entries){
System.out.println(entry.getKey() + ":" + entry.getValue());