集合类
为什么出现集合类
集合类的目的也是要存储多个数据,第一个反应应该使用数组完成,但是,数组有一个天生的短板,声明(创建)长度就是固定不可以改变(缺点),当操作的数据的长度是不固定,使用数组来完成的话,需要频繁的复制数组操作(经常改变索引)
数组优点:
- 存储数据结构是线性并且连续的数据结构(查询效率高)
- 数组的类型有两种:
- 存储的是基本数据类型:基本数据类型数组 ->相互之间不能进行转换
- 存储的是引用数据类型:引用数据类型数组->可以完成相互的转换
public class Demo02{
public static void main(String...args){
Integer[] arr01 = {100,200,300};
Object[] arr02 = arr01;//Integer[]=>Object[]
System.out.println(arr02);
}
}
集合类种类
-
java.util.Collection接口:
一个一个的存储数据 -
java.util.Map接口:
一对一对的存储数据
Collection接口
接口目的就是定义“标准”,约束你的行为
Interface Collection<E> : 是集合类的基础,实际开发中基本上不会使用,都会使用的是对应的子接口,但是它定义了关于单个数据(元素)操作的核心方法
Type Parameters:
E - the type of elements in this collection
java.util.Collection结构图(标红常用)
java.util.Collection
核心方法
演示核心方法,我们需要使用某一个实现类进行实例化才能演示,推荐使用java.util.ArrayList
集合类中的泛型只能是类,不能是基本数据类型,所有集合类只能存储的类型为引用数据类型
1.添加元素
Collection<Number> col1 = new ArrayList<>();
col1.add(100);//int->Integer->Number
Collection<String> col2 = new ArrayList<>();
col2.add("西游记");
col2.add("红楼梦");
//col1.addAll(col2);//无法存储
addAll源码:addAll(Collection<String extends Number> c)
col1中类型为Number,和col2中String没有关系,不存在上限关系,故col1.addAll(col2);//无法存储
2.判断为空,元素个数,清空元素
Collection<Number> col1 = new ArrayList<>();
System.out.println(col1.isEmpty());//判断为空
System.out.println(col1.size());//返回元素个数
col1.clear();//清空所有元素
3.查找操作(contains):是否包含某个元素(重要)★★★
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
Integer n1 = new Integer(100);
System.out.println(col1.contains(n1));//true
//为什么是true,我们需要去对应实现类ArrayList中实现的该方法
源码分析()
// col1.contains(n1) Integer->Object
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))//核心代码 Integer n1 = new Integer(100); equals比较内容
return i;
}
return -1;
}
contains底层调用equals方法
例题
import java.util.*;
public class Demo07{
public static void main(String...args){
Collection<Student> col = new ArrayList<>();
col.add(new Student("悟空",18));//匿名对象:使用一次
Student s1 = new Student("八戒",19);
col.add(s1);
col.add(new Student("唐僧",20));//匿名对象
Student s2 = new Student("唐僧",20);
System.out.println(col.contains(s1));//结果:true
System.out.println(col.contains(s2));//结果:false
}
}
class Student{
private String name;
private Integer sex;
public Student(String name,Integer sex){//构造方法初始化
this.name = name;
this.sex = sex;
}
}
分析:为什么col.contains(s2)
返回结果为false呢?通过关键的核心代码o.equals(elementData[i])
,Student类没有重写equals方法,Object类中提供的equals方法默认是比较的是内地址,所以返回false。假设你想返回true,那么我就需要Student重写equals方法(同时应该重写hashcode方法)
4.移除
1.删除单个元素
import java.util.*;
public class Demo08{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
col1.add(100);
Integer n1 = new Integer(100);
//移除找到第一个
System.out.println(col1.remove(n1));//也是使用equals比较
System.out.println(col1.remove(999));
System.out.println(col1.remove(n1));
System.out.println(col1);
}
}
2.删除多个元素
import java.util.*;
public class Demo09{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
col1.add(100);
Collection<Integer> col2 = new ArrayList<>();
col2.add(100);
col2.add(200);
col2.add(500);
col2.add(600);
col1.removeAll(col2);
System.out.println(col1);//[300, 400]
}
}
5.取交集未去重
import java.util.*;
public class Demo10{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
col1.add(100);
Collection<Integer> col2 = new ArrayList<>();
col2.add(100);
col2.add(200);
col2.add(500);
col2.add(600);
col1.retainAll(col2);
System.out.println(col1);//交集,没有去重
//结果:[100, 200, 100]
}
}
集合类元素的遍历
迭代器:是一种模式,它可以使得对于序列类型的数据结构进行遍历并且与被变量的对象分离(互不影响),我们不需要关心序列底层的结构是什么(ArrayList/LinkedList)样子,只要拿到这个对象,使用迭代器就可以遍历这个对象的内部。
使用迭代器遍历
import java.util.*;
public class Demo11{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
//获取迭代器
Iterator<Integer> iter = col1.iterator();
while(iter.hasNext()){
Integer value = iter.next();
System.out.println(value);
}
/*
System.out.println(iter.hasNext());
System.out.println(iter.next());//100
System.out.println(iter.hasNext());
System.out.println(iter.next());//200
System.out.println(iter.hasNext());
System.out.println(iter.next());//300
System.out.println(iter.hasNext());
System.out.println(iter.next());//400
System.out.println(iter.hasNext());
System.out.println(iter.next());//错误:java.util.NoSuchElementException
*/
}
}
普通for循环迭代器
import java.util.*;
public class Demo12{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
//获取迭代器
/*
for(初始化数据;循环条件;迭代条件){
循环体
}
*/
for(Iterator<Integer> iter = col1.iterator();iter.hasNext();){
Integer value = iter.next();
System.out.println(value);
}
}
}
增强for循环,底层也是通过迭代器实现
import java.util.*;
public class Demo13{
public static void main(String...args){
Collection<Integer> col1 = new ArrayList<>();
col1.add(100);
col1.add(200);
col1.add(300);
col1.add(400);
for(Integer value : col1){
System.out.println(value);
}
}
}
子接口
子接口List:存储的数据是有顺序
并且可以重复
List是一个子接口,继承了Collection的父接口(接口可以继承多个接口),对其进行了扩展,扩展了哪些方法?
源码:public interface List<E> extends Collection<E>
add(int index, E element) : 根据索引,添加到索引位置的数据 Create
boolean addAll(int index, Collection<? extends E> c) : 将集合添加到某个索引位置
★ E get(int index) : 通过索引获取数据,使用频率相当高 Read
indexOf(Object o) : 从前查找集合中是否包含元素,没有返回-1
lastIndexOf(Object o) :从后查找集合中是否包含元素,没有返回-1
listIterator() :更加丰富的迭代器
★ E remove(int index) : 移除指定索引位置是数据,容器中不在存在,返回移除的数据 Delete
set(int index, E element) :更新相应索引位置的数据 Update
★List<E> subList(int fromIndex, int toIndex) : 截取集合的内容
(1)ArrayList子类
源码定义:
public class ArrayList<E>
extends AbstractList<E> //抽象类只能被继承使用
implements List<E>, RandomAccess, Cloneable, Serializable
ArrayList底层是使用数组完成(数组创建之后固定长度),ArrayList是对象引用的一个可以改变的数组。
源码解释:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//一弄了一个空数组
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 0
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData/*引用传递*/, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//关键点int DEFAULT_CAPACITY = 10;
}
return minCapacity;
}
面试题:ArrayList默认开辟的数组长度是10:int DEFAULT_CAPACITY = 10;
添加数据
import java.util.*;
public class Demo01{
public static void main(String...args){
List<Integer> list = new ArrayList<>();//接口回调
list.add(300);
list.add(200);
list.add(400);
list.add(1,999);//原来索引1位置后的说有数据都需要移动
list.add(200);//是在集合末尾追加数据
System.out.println(list);
}
}
删除数据
import java.util.*;
public class Demo02{
public static void main(String...args){
List<String> list = new ArrayList<>();//接口回调
list.add("西游记");
list.add("红楼梦");
list.add("三国演义");
list.add("水浒传");
list.add("父母爱情");
list.add("四合院");
//当我们要删除指定元素一个一个删除的时候,没有问题
//list.remove(1);
//System.out.println(list);
//list.remove("水浒传");//继承Collection中的移除方法
//System.out.println(list);
}
}
一个一个删除可以用remove方法,但是当我们要删除索引为奇数的所有选项的时候
import java.util.*;
public class Demo04{
public static void main(String...args){
List<String> list = new ArrayList<>();//接口回调
list.add("0.西游记");
list.add("1.红楼梦");
list.add("2.三国演义");
list.add("3.水浒传");
list.add("4.父母爱情");
list.add("5.四合院");
for(int i=0;i<list.size();i++){
if(i%2!=0){
list.remove(i);
}
}
System.out.println(list);
}
}
改变了集合本身,所以该方法错误
正确代码:
import java.util.*;
public class Demo04{
public static void main(String...args){
List<String> list = new ArrayList<>();//接口回调
list.add("0.西游记");
list.add("1.红楼梦");
list.add("2.三国演义");
list.add("3.水浒传");
list.add("4.父母爱情");
list.add("5.四合院");
//删除索引为奇数的数据
/*
for(1.初始化条件;2.循环条件;4.迭代条件){
3.循环体
}
1->2->3->4 2->3->4
*/
/*
list使用ArrayList,底层的实现是可以改版的数组
数组:本身固定长度
list:底层创建的数组长度是6
*/
//这种方式改的是集合的本身
/*
for(int i=0;i<list.size();i++){
if(i%2!=0){
list.remove(i);
}
}
System.out.println(list);
*/
int index = 0;
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
iter.next();
if(index%2!=0){
iter.remove();
}
index++;
}
System.out.println(list);
}
}
注意:ArrayList默认数组长度为10,List默认长度为6
面试题1:ArrayList和Vector的区别
- 性能:Vector都使用
synchronized
进行修饰- Vector采用的是同步处理方式,性能比较低
- ArrayList采用的是异步处理方式,性能更高
- 线程安全:
- Vector线程是安全
- ArrayList非线程安全
- 遍历方式:
- Vector:迭代器和for循环或者Enumeration(Java Web阶段->这种遍历)
- ArrayList:迭代器和for循环
(2)LinledList子类
源码定义:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
重点:LinkedList表示的是一个双向链表,通过静态内部类建立
源码解释:
private static class Node<E> {
E item;//数据本身
Node<E> next;//下一个节点
Node<E> prev;//上一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
面试题2:ArrayList和LinkedList的比较
- 实现方法:ArrayList底层是使用的有序的数组实现,LinkedList是使用双向链表实现
- 查询效率:ArrayList的查询效率是高于LinkedList
- 删除和修改:如果是频繁的改变索引的情况下,LinkedList的效率是远远高于ArrayList
- 添加形式是追加:ArrayList是高于LinkedList
优先推荐使用:java.util.ArrayList
子接口Set:存储的数据是无序
并且`不可以重复
无序:是指没有按照你存储的顺序存储数据
子接口Set没有扩展Collection父接口的,方法没有增加。
(1)散列的存放:HashSet
import java.util.*;
public class Demo07{
public static void main(String...args){
List<String> list = new ArrayList<>();
list.add("D");
list.add("A");
list.add("E");
list.add("B");
list.add("A");
System.out.println(list);
Set<String> set = new HashSet<>();
set.add("D");
set.add("A");
set.add("E");
set.add("B");
set.add("A");
System.out.println("A".hashCode());
System.out.println("B".hashCode());
System.out.println("D".hashCode());
System.out.println("E".hashCode());
System.out.println(set);//显示的时候排序,自然排序 从小到大
}
}
对象的比较内容,需要重写equals方法
结果:
(2)有序的存放:LinkedHashSet
import java.util.*;
public class Demo09{
public static void main(String...args){
Set<String> set = new LinkedHashSet<>();
set.add("D");
set.add("A");
set.add("E");
set.add("B");
set.add("A");
System.out.println(set);//
}
}
结果:
(3)有排序功能:TreeSet
TreeSet源码构造方法中有一个接口
默认情况下是自然排序
import java.util.*;
public class Demo10{
public static void main(String...args){
Set<String> set = new TreeSet<>();
set.add("D");
set.add("A");
set.add("E");
set.add("B");
set.add("A");
System.out.println(set);//升序
//Set<String> set1 = new TreeSet<>(new AComparator());
Set<String> set1 = new TreeSet<>(new Comparator<String>(){//匿名实现类
public int compare(String o1, String o2) {
//return o1.hashCode()-o2.hashCode();
return o2.hashCode()-o1.hashCode();
}
});
set1.add("D");
set1.add("A");
set1.add("E");
set1.add("B");
set1.add("A");
System.out.println(set1);//降序
}
}
结果:
两种排序方式:
- 自然排序:要在自定义类中实现Comparerable接口 ,并且重写compareTo方法
- 比较器排序:在自定义类中实现Comparetor接口,重写compare方法
Map接口
Map结构图
例子:统计信息
import java.util.*;
import java.util.zip.DeflaterOutputStream;
public class F07 {
static List<Map<String,Object>> list = new ArrayList<>();//共享数据
static{
Map<String,Object> map1 = new HashMap<>();
map1.put("姓名","叶凡");
map1.put("性别","男");
map1.put("境界","红尘仙");
map1.put("世界","遮天");
System.out.println(map1);KEY的部分底层就是使用的是HashSet集合
list.add(map1);
//map2中的key需要跟map1中的KEY保持一致,如果不一致,那么导致的就多出来属性
Map<String,Object> map2 = new HashMap<>();
map2.put("姓名","石昊");
map2.put("性别","男");
map2.put("境界","仙帝");
map2.put("世界","九天十地");
list.add(map2);
Map<String,Object> map3 = new HashMap<>();
map3.put("姓名","陆羽");
map3.put("性别","男");
map3.put("境界","主宰");
map3.put("世界","鸿蒙宇宙");
list.add(map3);
System.out.println("list = " + list);
}
public static void main(String[] args) {
System.out.println("姓名\t性别\t境界\t世界");
for (int i = 0; i < list.size(); i++) { //对List进行遍历,那么我们应该是使用带索引去计算序号
Map<String,Object> map = list.get(i);
System.out.print(map.get("姓名")+"\t");//通过KEY获取Value
System.out.print(map.get("性别")+"\t");
System.out.print(map.get("境界")+"\t");
System.out.println(map.get("世界"));
}
}
}
Map常用方法
(1)判断Map是否为空,长度,清空方法
import java.util.*;
public class Demo02{
public static void main(String...args){
//判断Map是否为空
Map<String,String> map = null;
//编译阶段:map处于编译时类型,只能使用该类型中定义的方法
//System.out.println(map.isEmpty());//1.编译能通过 2.运行是空指针异常
map = new HashMap<>();
System.out.println(map.isEmpty());
map.put("姓名","悟空");
map.put("性别","男");
map.put("年龄","18");
map.put("出生日期","2020-01-01");
System.out.println(map.isEmpty());
System.out.println(map.size());
map.remove("年龄");//移除KEY
System.out.println(map.size());
map.clear();
System.out.println(map.isEmpty());
System.out.println(map.size());
}
}
(2)是否包含KEY,VALUE
import java.util.*;
public class Demo03{
public static void main(String...args){
Map<Object,Object> map = new HashMap<>();
map.put("姓名","悟空");
map.put(999,"男");
map.put("年龄",18);
map.put("出生日期","2020-01-01");
Integer number = new Integer(999);
System.out.println(map.containsKey(number));//true
String str = new String("悟空");
System.out.println(map.containsValue(str));//true
//无论是KEY和VALUE的任何操作,都是比较的内容,Integer和String重写了equals方法
}
}
(3)关于KEY的设置,我们推荐使用String
import java.util.*;
public class Demo04{
public static void main(String...args){
Map<Student,String> map = new HashMap<>();//实际开发中我没有遇到使用自定义类作为KEY的时候
map.put(new Student("悟空"),"100");
map.put(new Student("悟空"),"100");
map.put(new Student("悟空"),"100");
map.put(new Student("悟空"),"100");
System.out.println(map.size());//结果是4,因为KEY默认使用HashSet,Student没有重写equals方法,比较地址
}
}
class Student{
private String name;
public Student(String name){
this.name = name;
}
}
注意: KEY默认使用HashSet,Student没有重写equals方法,比较地址
(5)排序(与存放顺序无关)
import java.util.*;
public class Demo05{
public static void main(String...args){
Map<String,Object> map = new HashMap<>();
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
//输出Map数据的时候,是按照KEY的自然排序
System.out.println(map);
}
}
1.若是想按照存放顺序输出,则应该用**LinkedHashMap<>**来实现
import java.util.*;
public class Demo06{
public static void main(String...args){
Map<String,Object> map = new LinkedHashMap<>();
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
//输出Map数据的时候,LinkedHashSet
System.out.println(map);
}
}
2.Map默认升序,用TreeMap实现降序输出
import java.util.*;
public class Demo07{
public static void main(String...args){
Map<String,Object> map = new TreeMap<>();
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
//输出Map数据的时候,是按照升序操作
System.out.println(map);
map = new TreeMap<>(new Comparator<String>(){
public int compare(String o1,String o2){
return o2.hashCode()-o1.hashCode();
}
});
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
System.out.println(map);
}
}
Map遍历(面试题)
(1)思路:通过遍历KEY,来获取对应的VALUE:常用方式(可以控制显示顺序)
import java.util.*;
public class Demo08{
public static void main(String...args){
Map<String,Object> map = new HashMap<>();
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
Set<String> keySet = map.keySet();//存储的是所有的KEY
//关于Set集合的遍历方式:
System.out.println("1.简单方式:增强for循环");
for(String key : keySet){
System.out.println(key+"\t"+map.get(key));//通过KEY获取VALUE
}
System.out.println("2.简单方式:迭代器的循环");
Iterator<String> iter1 = keySet.iterator();
System.out.println("1.常用的是while循环");
while(iter1.hasNext()){
String key = iter1.next();
Object value = map.get(key);
System.out.println(key+"---"+value);
}
System.out.println("2.for循环");
for(Iterator<String> iter2= keySet.iterator();iter2.hasNext();){
String key = iter2.next();
Object value = map.get(key);
System.out.println(key+"****"+value);
}
}
}
(2)直接遍历VALUE,不太常用
import java.util.*;
public class Demo09{
public static void main(String...args){
Map<String,Object> map = new HashMap<>();
map.put("D","悟空");
map.put("A","男");
map.put("C",18);
map.put("B","2020-01-01");
Collection<Object> col = map.values();//获取所有的values
for(Object value : col){
System.out.println(value);
}
}
}
(3)思路:遍历`键值对
由于Map中存放的元素均为键值对,故每一个键值对必然存在一个映射关系。
Map中采用Entry内部类来表示一个映射项,映射项包含Key和Value (我们总说键值对键值对, 每一个键值对也就是一个Entry)
import java.util.*;
public class F07 {
public static void main(String[] args) {
Map<String,Object> map3 = new HashMap<>();
map3.put("姓名","陆羽");//映射
map3.put("性别","男");//键值对就是mapping
map3.put("境界","主宰");
map3.put("世界","鸿蒙宇宙");
//获取键值对你集合
Set<Map.Entry<String,Object>> entrySet = map3.entrySet();
System.out.println("输出键值对数量"+entrySet.size());
for (Map.Entry<String,Object> entry : entrySet){
System.out.println("entry就是键值对"+entry);
System.out.println("单独获取K"+entry.getKey());
System.out.println("单独获取K"+entry.getValue());
}
}
}
输出结果:
(4)JDK8以后的提供的语法
import com.sun.jdi.Value;
import java.security.Key;
import java.util.*;
public class F07 {
public static void main(String[] args) {
Map<String,Object> map3 = new HashMap<>();
map3.put("姓名","陆羽");//映射
map3.put("性别","男");//键值对就是mapping
map3.put("境界","主宰");
map3.put("世界","鸿蒙宇宙");
map3.forEach((key,value)->{
System.out.println(key);
System.out.println(value);
});
map3.forEach((k,v)->System.out.println(k+"--"+v));
}
}