集合
![](https://i-blog.csdnimg.cn/blog_migrate/ec3fa121bdf1da5fe0815bd73e6497ee.png)
Collection(单列集合)(接口)
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() | 返回集合中元素的个数 | 返回集合中元素的个数 |
remove 因为Collection里面定义的是共性方法,所以此时不能通过索引进行删除,只能通过元素的对象进行删除
contains 底层是依赖equals方法进行判断是否存在,如果集合存储的是自定义的对象,也想通过此方法判断是否包含,那么在javabean类中一定要重写equals方法
Collection的遍历方式
迭代器在java中的类是Iterator,迭代器是集合专用的遍历方式
Iterator<E> iterator() //返回迭代器对象,默认指向当前集合的0索引
方法名称 | 说明 |
boolean hasNext() | 判断当前位置是否有元素,有元素返回true,没有元素返回false |
E next() | 获取当前位置的元素,并将迭代器对象移向下一个位置 |
迭代器遍历
Iterator<String> it = list.iterator(); //创建指针
while(it.hasNext()){ //判断是否有元素
String s = it.next; //获取元素 移动指针
System.out.println(s);
}
迭代器源码分析
注意
迭代器遍历完毕,指针不会复位
迭代器遍历时,不能用集合的方法进行增加或者删除
增强for的底层就是迭代器,为了简化迭代器的代码书写的
所有的单列集合和数组才能用增强for进行遍历
//格式
//s是一个第三方变量,修改了s的值不会改变集合中原本的数据
for(元素的数据类型 变量名(s) : 数组或者集合){
}
lambda表达式遍历
//使用匿名内部类
//底层它会自己遍历集合,依次得到每一个元素,把得到的每一个元素,传递给下面的accept方法
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//使用lambda表达式
c.forEach(s -> System.out.println(s));
List(接口)
List系列集合:添加的元素是有序、可重复、有索引
Collection的方法List都继承了
List因为有索引,所以多了很多索引操作的方法
方法名称 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引的元素 |
List遍历方法
除了刚刚通用的遍历方法外,List集合还可以用普通for遍历和listIterator进行遍历,listIterator继承了Iterator这个接口,其中的方法基本相同
ArrayList
ArrayList集合底层原理
利用空参创建的集合,在底层创建一个默认长度为0的数组
添加第一个元素时,底层会创建一个新的长度为10的数组
![](https://i-blog.csdnimg.cn/blog_migrate/d0f59caf5cda66c27e1af732c9a96e72.png)
存满时,会扩容1.5倍
![](https://i-blog.csdnimg.cn/blog_migrate/dfd1d7f66937ca67916987a3b10c5ca5.png)
如果一次添加多个元素,1.5倍还放不下,则新创建的数组以实际·为准
![](https://i-blog.csdnimg.cn/blog_migrate/2fd6c6c90934414a07d45a56a4d968d2.png)
LinkedList
底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的
LinkList本身多了很多直接操作首尾元素的特有API
特有方法 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表的第一个元素 |
public E getLast() | 返回此列表的最后一个元素 |
public E removeFirst() | 从此列表删除并返回第一个元素 |
public E removeLast() | 从此列表删除并返回最后一个元素 |
Vector
知道存在即可,已经被淘汰
Set(接口)
Set系列集合:添加的元素是无序、不重复、无索引
Set接口中的方法基本上与Collection的API一致
HashSet
无序、不重复、无索引
HashSet集合底层采取哈希表存储结构
哈希表是一种对于增删改查数据性能都较好的结构
哈希值
根据hashCode方法算出来的int类型的整数
该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值的特点、
如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值是相同的
在小部分情况下,不同属性值的地址值计算出来的哈希值也有可能一样(哈希碰撞)
HashSet底层原理
![](https://i-blog.csdnimg.cn/blog_migrate/a279a2af42d9999bc734a19d21abc34f.png)
加载因子就是数组的扩容时机,当数组中存了16*0.75=12个元素时,数组就会扩容成原来的两倍
存入的位置:int index = (数组长度 - 1 & 哈希值)
当链表长度大于8而且数组长度大于等于64时当前链表自动转成红黑树
如果集合中存储的是自定义对象,必须要重写hashCode和equals方法
LinkedHashSet
有序、不重复、无索引
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依赖哈希表,只是每个元素有额外的多了一个双链表的机制记录存储的顺序
![](https://i-blog.csdnimg.cn/blog_migrate/aa7edc80026e7aa9a4314e9445e4838c.png)
TreeSet
可排序、不重复、无索引
可排序:按照元素的默认规则(由小到大)排序
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
TreeSet的两种比较方式
方式一:默认排序/自然排序:javabean类实现Comparable接口指定比较规则
//学生类
public class Student implements Comparable<Student>{
...
...
...
@Override
//this:表示当前要添加的元素
//o:表示已经在红黑树存在的元素
//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0 :表示当前要添加的元素已经存在,舍弃
public int compareTo(Student o) {
System.out.println("--------------");
System.out.println("this:" + this);
System.out.println("o:" + o);
//指定排序的规则
//只看年龄,我想要按照年龄的升序进行排列
return this.getAge() - o.getAge();
}
}
public static void main(String[] args) {
/*
需求:创建TreeSet集合,并添加3个学生对象
学生对象属性:
姓名,年龄。
要求按照学生的年龄进行排序
同年龄按照姓名字母排列(暂不考虑中文)
同姓名,同年龄认为是同一个人
方式一:
默认的排序规则/自然排序
Student实现Comparable接口,重写里面的抽象方法,再指定比较规则
*/
//1.创建三个学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("zhaoliu",26);
//2.创建集合对象
TreeSet<Student> ts = new TreeSet<>();
//3.添加元素
ts.add(s3);
ts.add(s2);
ts.add(s1);
ts.add(s4);
//4.打印集合
System.out.println(ts);
![](https://i-blog.csdnimg.cn/blog_migrate/59d3dbec1acc053613974fdadca5563e.png)
方式二:比较器排序:创建TreeSet对象的时候,传递比较器Comparator指定规则
public static void main(String[] args) {
/*
需求:请自行选择比较器排序和自然排序两种方式;
要求:存入四个字符串, “c”, “ab”, “df”, “qwer”
按照长度排序,如果一样长则按照首字母排序
采取第二种排序方式:比较器排序
*/
//1.创建集合
//o1:表示当前要添加的元素
//o2:表示已经在红黑树存在的元素
//返回值规则跟之前是一样的
// TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// // 按照长度排序
// int i = o1.length() - o2.length();
// //如果一样长则按照首字母排序
// i = i == 0 ? o1.compareTo(o2) : i;
// return i;
// }
// });
TreeSet<String> ts = new TreeSet<>((o1, o2)->{
// 按照长度排序
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
});
//2.添加元素
ts.add("c");
ts.add("ab");
ts.add("df");
ts.add("qwer");
//3.打印集合
System.out.println(ts);
}
//学生类
public class Student2 implements Comparable<Student2> {
...
...
...
/* 按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存。*/
@Override
public int compareTo(Student2 o) {
int sum1 = this.getChinese() + this.getMath() + this.getEnglish();
int sum2 = o.getChinese() + o.getMath() + o.getEnglish();
//比较两者的总分
int i = sum1 - sum2;
//如果总分一样,就按照语文成绩排序
i = i == 0 ? this.getChinese() - o.getChinese() : i;
//如果语文成绩一样,就按照数学成绩排序
i = i == 0 ? this.getMath() - o.getMath() : i;
//如果数学成绩一样,按照英语成绩排序(可以省略不写)
i = i == 0 ? this.getEnglish() - o.getEnglish() : i;
//如果英文成绩一样,按照年龄排序
i = i == 0 ? this.getAge() - o.getAge() : i;
//如果年龄一样,按照姓名的字母顺序排序
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
public static void main(String[] args) {
/* 需求:创建5个学生对象
属性:(姓名,年龄,语文成绩,数学成绩,英语成绩),
按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英文成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存。
第一种:默认排序/自然排序
第二种:比较器排序
默认情况下,用第一种排序方式,如果第一种不能满足当前的需求,采取第二种方式。
课堂练习:
要求:在遍历集合的时候,我想看到总分。
*/
//1.创建学生对象
Student2 s1 = new Student2("zhangsan",23,90,99,50);
Student2 s2 = new Student2("lisi",24,90,98,50);
Student2 s3 = new Student2("wangwu",25,95,100,30);
Student2 s4 = new Student2("zhaoliu",26,60,99,70);
Student2 s5 = new Student2("qianqi",26,70,80,70);
//2.创建集合
TreeSet<Student2> ts = new TreeSet<>();
//3.添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//4.打印集合
//System.out.println(ts);
for (Student2 t : ts) {
System.out.println(t);
}
}
Map(双列集合)(接口)
双列集合的特点
双列集合一次需要存一对数据,分别为键和值
键不能重复,值可以重复
键和值是一一对应的,每一个键只能找到自己对应的值
键+值这个整体称之为”键值对“或者”键值对对象“,在Java中叫做”Entry对象"
方法名称 | 说明 |
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
在添加数据的时候,如果键不存在,那么直接把键值对对象添加到集合中,方法返回null;如果键是存在的,那么会把原来的键值对对象覆盖,会把覆盖的值进行返回
Map集合的遍历
键找值
//获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = map.keySet();
for (String key : keys) {
//利用map集合中的键获取对应的值
String value = map.get(key);
System.out.println(key + "=" + value);
键值对
//通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//遍历entries集合,得到每一个键值对对象
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
Lambda表达式
map.forEach((key, value) -> { System.out.println(key + "=" + value);});
HashMap
特点都是由键决定的:无序、不重复、无索引
HashMap底层是哈希表结构
如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储自定义对象则不需要重写
LinkedHashMap
有序、不重复、无索引
原理和LinkedHashSet相同
TreeMap
不重复、无索引、可排序:对键进行排序
原理与TreeSet相同
排序规则
实现Comparable接口,指定比较规则
/**
* 键:学生对象
* 值:籍贯
* 要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人
*/
TreeMap<Student,String> m = new TreeMap<>();
Student s1 = new Student("zhangsan",21);
Student s2 = new Student("lisi",23);
Student s3 = new Student("wangwu",20);
Student s4 = new Student("zhaoliu",20);
m.put(s1,"河南");
m.put(s2,"北京");
m.put(s3,"上海");
m.put(s4,"江苏");
System.out.println(m);
//学生类
public class Student implements Comparable<Student>{
...
...
...
@Override
public int compareTo(Student o) {
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
创建集合时传递Comparator比较器对象,指定比较规则
/**
* 字符串”aababcabdcdcabe“
* 统计字符串中每一个字符出现的次数,并按照一下格式输出
* 输出结果:a(5) b(4) c(3) d(2) e(1)
*/
String s = "aababcabdcdcabe";
TreeMap<String,Integer> tm = new TreeMap<>((o1,o2) -> {
return o1.compareTo(o2);
}
);
for (int i = 0; i < s.length(); i++) {
String c = s.charAt(i) + "";
if (tm.containsKey(c)){
int value = tm.get(c);
value = value + 1;
tm.put(c ,value);
}else {
tm.put(c ,1);
}
}
StringBuffer sb = new StringBuffer();
tm.forEach((key,value)->{
sb.append(key).append(" (").append(value).append(") ");
});
System.out.println(sb);