JAVA 集合进阶(十三)

集合进阶(十三)

集合体系结构

  • 单列集合Collection
    • List(接口):添加的元素是有序(指存和取顺序一样),可重复(可存在重复的元素),可索引(可通过索引获取元素)
      • ArrayList(实现类)
      • LinkedList(实现类)
      • Vector(已淘汰)(实现类)
    • Set(接口):添加的元素是无序(指存和取顺序可能不一样),不重复(不能存储重复的元素),无索引(不可通过索引获取元素,不可使用普通for循环遍历)
      • HashSet(实现类)->LinkedHashSet(实现类)
      • TreeSet(实现类)
  • 双列集合Map
    • HashMap (实现类)->LinkedHashMap(实现类)
    • Hashtable(实现类)->Properties(实现类)
    • TreeMap(实现类)

泛型

若并未个集合指定类型,默认数据类型为Object类,此时可以给集合添加任意数据类型,但我们获取数据时,无法使用子类特有的行为。

泛型:是JDK5中引入的特性,可在编译阶段约束操作的数据类型,并进行检查。

格式:<引用数据类型>

好处:统一数据类型,将运行时期的问题提前到编译期间,避免强制类型转换可能出现的异常,因此在编译阶段就能确定下来,并且能调用特有的行为。

细节

  • 泛型中不能写基本数据类型,需将基本数据类型转换成对应的包装类
  • 指定泛型的具体类型后,传送数据时,可传送该类类型或其子类类型
  • 若不写泛型,类型默认是Object
  • 泛型可在方法接口后进行定义,即泛型类泛型方法泛型接口

泛型类

当一个类中,某个变量的数据类型不确定,可定义泛型类。
泛型类在创建对象时,会确定具体类型

格式

修饰符 class 类名<E>{

}
//此处E可以理解为记录数据类型的对象,可写为:T,E,K,V等

范例

//MyArrayList.java
import java.util.Arrays;
public class MyArrayList<E> {
    int size;
    Object[] objects=new Object[10];
    public boolean add(E e){
        objects[size]=e;
        size++;
        return true;
    }
    public E get(int index){
        return (E)objects[index];
    }

    @Override
    public String toString() {
        return Arrays.toString(objects);
    }
}

//Test.java
public class Test{
    public static void main(String[] args) {
        MyArrayList<String> myArrayList = new MyArrayList<>();
        myArrayList.add("aaa");
        myArrayList.add("bbb");
        myArrayList.add("ccc");
        System.out.println(myArrayList);
        System.out.println(myArrayList.get(1));
    }
}
/*
[aaa, bbb, ccc, null, null, null, null, null, null, null]
bbb
*/

泛型方法

方法中形参类型不确定时:

  1. 使用类名后面定义的泛型。(所有方法都能用)
  2. 在方法申明上定义自己的泛型。(只有本方法能用)

格式

修饰符<T> 返回值类型 方法名(类型 变量名){

}
//此处T可以理解为记录数据类型的对象,可写为:T,E,K,V等
//非静态方法:内部的泛型,会根据类的泛型去匹配
//静态方法:静态方法中若加入泛型,必须声明出自己的独立的泛型,在调用方法传入参数时,会确定具体的类型

范例

//定义一个工具类,ListUtil
//类中定义一个静态方法addAll,用来添加另一个集合中的元素
//ListUtil.Java
import java.util.ArrayList;
public class ListUtil {
    private ListUtil(){}
    public static<T> boolean addALL(ArrayList<T> list1, ArrayList<T> list2){
        for(int i=0;i<list2.size();i++){
            list1.add(list2.get(i));
        }
        return true;
    }
}
//test.java
import java.util.ArrayList;
public class Test{
    public static void main(String[] args) {
        ArrayList<String> arrayList=new ArrayList<>();
        arrayList.add("aaa");
        arrayList.add("bbb");
        arrayList.add("ccc");
        ArrayList<String> arrayList1=new ArrayList<>();
        arrayList1.add("ddd");
        arrayList1.add("eee");
        arrayList1.add("fff");
        ListUtil.addALL(arrayList, arrayList1);
        System.out.println(arrayList);
    }
}
/*
[aaa, bbb, ccc, ddd, eee, fff]
*/

泛型接口

格式

修饰符 interface 接口名<类型>{

}

使用方式

  1. 实现类给出具体的类型,创造实现类的对象时不需要给出泛型
  2. 实现类延续泛型创建实现类对象时再确定类型,创造实现类的对象时需要给出泛型

方式一范例

//接口类
//ListAdd.java
public interface ListAdd<E>{
    public abstract void add(E e);
}

//实现类
//MyArrayList.java
import java.util.Arrays;
public class MyArrayList implements ListAdd<String> {
    Object[] objects=new Object[10];
    int size=0;
    @Override
    public void add(String s) {
        objects[size]=s;
        size++;
    }

    @Override
    public String toString() {
        return Arrays.toString(objects);
    }
}

//测试类
//Test.java
public class Test{
    public static void main(String[] args) {
        MyArrayList myArrayList=new MyArrayList();
        myArrayList.add("aaa");
        myArrayList.add("bbb");
        myArrayList.add("ccc");
        System.out.println(myArrayList);
    }
}
/*
[aaa, bbb, ccc, null, null, null, null, null, null, null]
*/

方式二范例

//接口类
//ListAdd.java
public interface ListAdd<E>{
    public abstract void add(E e);
}

//实现类
//MyArrayList.java
import java.util.Arrays;
public class MyArrayList<E> implements ListAdd<E> {
    Object[] objects=new Object[10];
    int size=0;
    @Override
    public void add(E e) {
        objects[size]=e;
        size++;
    }

    @Override
    public String toString() {
        return Arrays.toString(objects);
    }
}

//测试类
//Test.java
public class Test{
    public static void main(String[] args) {
        MyArrayList<String> myArrayList=new MyArrayList();
        myArrayList.add("aaa");
        myArrayList.add("bbb");
        myArrayList.add("ccc");
        System.out.println(myArrayList);
    }
}
/*
[aaa, bbb, ccc, null, null, null, null, null, null, null]
*/

通配符

通配符可以限定类型的范围

泛型不具备继承性,但泛型的数据具备继承性

import java.util.ArrayList;
public class Test{
    public static void main(String[] args) {
        ArrayList<Ye> list1=new ArrayList<>();
        ArrayList<Fu> list2=new ArrayList<>();
        ArrayList<Zi> list3=new ArrayList<>();
        //泛型不具有继承性
        //method(list1);
        //method(list2);会报错
        //method(list3);会报错
        //数据具有继承性
        list1.add(new Ye());
        list1.add(new Fu());
        list1.add(new Zi());
    }
    public static void method(ArrayList<Ye> list){

    }
}
class Ye{

}
class Fu extends Ye{

}
class Zi extends Fu{

}

通配符

**?**表示不确定的类型,也可以进行类型的限定

  • ? extends E
    • 表示可以传递E或E所有的子类类型
  • ?super E
    • 表示可以传递E或者E的所有父类类型
package com.ljsblog.domain12;

import java.util.ArrayList;
public class Test{
    public static void main(String[] args) {
        ArrayList<Ye> list1=new ArrayList<>();
        ArrayList<Fu> list2=new ArrayList<>();
        ArrayList<Zi> list3=new ArrayList<>();
        method1(list1);
        method1(list2);
        method1(list3);

        method2(list1);
        method2(list2);
        method2(list3);
    }
    public static void method1(ArrayList<? extends Ye> list){

    }
    public static void method2(ArrayList<? super Zi> list){

    }
}
class Ye{

}
class Fu extends Ye{

}
class Zi extends Fu{

}

使用场景

  1. 若在定义类,方法,接口时,类型不确定,就可以定义,泛型类,泛型方法,泛型接口。
  2. 若类型不确定,但只传递某个继承体系,可使用泛型的通配符

范例

/*需求
	定义一个继承结构:
										   动物
									  /            \
									 猫	           狗
							       /    \        /    \
	                           波斯猫    狸花猫   泰迪  哈士奇
	属性:名字,年龄
	行为:吃东西
	波斯猫方法体打印:一只叫做XXX的,X岁的波斯猫,正在吃小饼干
	狸花猫方法体打印:一只叫做xxx的,x岁的狸花猫,正在吃鱼
	泰迪方法体打印:一只叫做xxx的,x岁的泰迪,正在吃骨头,边吃边蹭
	哈士奇方法体打印:一只叫做XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家
	测试类中定义一个方法用于饲养动物
	public static void keepPet(ArrayList<???> list){
		遍历集合,调用动物的eat方法
	}
	要求1:该方法能养所有品种的猫,但是不能养狗
	要求2:该方法能养所有品种的狗,但是不能养猫
	要求3:该方法能养所有的动物,但是不能传递其他类型
*/


//Animal.java
public abstract class Animal {
    private String name;
    private int age;
    public Animal(){}
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age=age;
    }
    public int getAge(){
        return age;
    }
    public abstract void eat();
}

//Dog.java
public abstract class Dog extends Animal{
    public Dog() {
    }
    public Dog(String name, int age) {
        super(name, age);
    }
}

//Cat.java
public abstract class Cat extends Animal {
    public Cat(){}
    public Cat(String name,int age){
        super(name,age);
    }
}

//Bosi.java
public class Bosi extends Cat {
    public Bosi(){}
    public Bosi(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的波斯猫,正在吃小饼干");
    }
}

//Lihua.java
public class Lihua extends Cat {
    public Lihua() {
    }

    public Lihua(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的狸花猫,正在吃鱼");
    }
}

//Taidi.java
public class Taidi extends Dog{
    public Taidi() {
    }

    public Taidi(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的泰迪,正在吃骨头,边吃边蹭");
    }
}


//Hashiqi.java
public class Hashiqi extends Dog{
    public Hashiqi() {
    }

    public Hashiqi(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的哈士奇,正在吃骨头,边吃边拆家");
    }
}

//Test.java
import java.util.ArrayList;
public class Test{
    public static void main(String[] args) {
        ArrayList<Bosi> list1=new ArrayList<>();
        Bosi bosi1=new Bosi("波斯猫甲",7);
        Bosi bosi2=new Bosi("波斯猫乙",9);
        list1.add(bosi1);
        list1.add(bosi2);
        keepPet1(list1);

        ArrayList<Lihua> list2=new ArrayList<>();
        Lihua lihua1=new Lihua("狸花猫甲",1);
        Lihua lihua2=new Lihua("狸花猫乙",2);
        list2.add(lihua1);
        list2.add(lihua2);
        keepPet1(list2);

        ArrayList<Hashiqi> list3=new ArrayList<>();
        Hashiqi hashiqi1=new Hashiqi("哈士奇甲",2);
        Hashiqi hashiqi2=new Hashiqi("哈士奇乙",3);
        list3.add(hashiqi1);
        list3.add(hashiqi2);
        keepPet2(list3);

        ArrayList<Taidi> list4=new ArrayList<>();
        Taidi taidi1=new Taidi("泰迪甲",12);
        Taidi taidi2=new Taidi("泰迪乙",6);
        list4.add(taidi1);
        list4.add(taidi2);
        keepPet2(list4);

        keepPet3(list1);
        keepPet3(list2);
        keepPet3(list3);
        keepPet3(list4);
    }
    //要求1:该方法能养所有品种的猫,但不能养狗
    public static void keepPet1(ArrayList<? extends Cat> list){
        for(int i=0;i<list.size();i++){
            list.get(i).eat();
        }
    }
    //要求2:该方法能养所有品种的狗,但不能养猫
    public static void keepPet2(ArrayList<? extends Dog> list){
        for(int i=0;i<list.size();i++){
            list.get(i).eat();
        }
    }
    //要求3:该方法能养所有动物
    public static void keepPet3(ArrayList<? extends Animal> list){
        for(int i=0;i<list.size();i++){
            list.get(i).eat();
        }
    }
}

数据结构

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。

常见的数据结构

  1. 队列
  2. 数组
  3. 链表
  4. 二叉树
  5. 二叉查找树
  6. 平衡二叉树
  7. 红黑树

只能在表的一个固定端进行数据结点的插入和删除操作。

特点:后进先出,先进后出。

先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。

数据进入栈模型的过程称为:压/进栈

数据离开栈模型的过程称为:弹/出栈

队列

队列只允许在表的一端进行插入操作,而在另一端进行删除操作。

特点:先进先出,后进后出。

进行插入操作的一端称为队尾,进行删除操作的一端称为队头。

数据从队尾进入队列模型的过程称为:入队列

数据从队头离开队列模型的过程称为:出队列

数组

特点

  • 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同(元素在内存中是连续存储的)。
  • 删除效率低:要将原始数据删除,同时后面每个数据前移
  • 添加效率极低:添加位置的每个数据后移,再添加数据。

链表

链表中的结点是独立的对象,在内存中是不连续的。
单向链表每个节点包含数据值下个结点的地址
双向链表每个节点包含上个结点的地址数据值下个结点的地址
链表查询慢,无论查询哪个数据都要从头找,但首尾操作极快。
链表增删相对较快。

二叉树

二叉树是指树中节点(结点)的不大于2的有序树,即二叉树是每个结点最多有两个子树的树结构。通常子树被称作左子树右子树
二叉树的结点包括父节点地址左子节点地址右子节点地址
:每个节点的子节点数量。
树高:树的总层数。
根节点:最顶层的结点。
左子节点:左下方的结点。
右子节点:右下方的结点。

二叉查找树

二叉查找树,又称二叉排序树或二叉搜索树。

特点

  1. 每个结点上最多有两个子节点
  2. 任意结点左子树上的值都小于当前节点
  3. 任意结点右子树上的值都大于当前节点

添加结点规则

  1. 小的存左边
  2. 大的存右边
  3. 一样的不存

弊端:二叉查找树可能退化成链表,相应的,二叉搜索树的查找操作是和这棵树的高度相关的,而此时这颗树的高度就是这颗树的节点数n。

平衡二叉树

任意结点的左右子树高度不超过1,任意结点的左右两个子树都是一颗平衡二叉树。

遍历方式

所有二叉树都可使用

  1. 前序遍历:从根节点开始,按照当前节点,左子节点,右子节点的顺序遍历。
  2. 中序遍历:从最左边的子节点开始,按照左子节点,当前节点,右子节点的顺序遍历。
  3. 后续遍历:从最左边的子节点开始,按照左子节点,右子节点,当前节点的顺序遍历。
  4. 层序遍历:从根节点开始一层一层遍历。

旋转机制:左旋和右旋。

触发时机:当添加一个结点之后,该数不再是一颗平衡二叉树

左旋

确定支点:从添加的结点开始,不断的往父节点找不平衡的节点。

步骤

  1. 以不平衡的点为支点
  2. 把支点左旋降级,变成左子节点
  3. 晋升原来的右子节点

  1. 以不平衡的点为支点
  2. 将根节点的右侧向左拉
  3. 原本的右子节点变成新的父节点,并把多余的左子节点出让,给已降级的根节点当右子节点
右旋

确定支点:从添加的结点开始,不断的往父节点找不平衡的节点。

步骤

  1. 以不平衡的点为支点
  2. 把支点右旋降级,变成右子节点
  3. 晋升原来的左子节点

  1. 以不平衡的点为支点
  2. 将根节点的左侧向右拉
  3. 原本的左子节点变成新的父节点,并把多余的右子节点出让,给已降级的根节点当左子节点
需旋转的四种情况
  1. 左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡,需进行一次右旋
  2. 左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡,需先局部左旋,再整体右旋
  3. 右右:当根节点右子树的右子树有节点插入,导致二叉树不平衡,需进行一次左旋
  4. 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡,需先局部右旋,再整体左旋
红黑树

红黑树:是一种自平衡的二叉查找树,是一种特殊的二叉查找树,红黑树上的每一个结点都有存储位表示结点的颜色,每一个结点可以是红或者黑,红黑树不是高度平衡的,它的平衡是通过红黑规则进行实现的。

红黑树增删改查的性能都很好。

节点父节点地址左子节点地址右子节点地址颜色

红黑规则

  1. 每个节点或是红色,或是黑色
  2. 根节点必须是黑色
  3. 若一个节点没有子节点或父节点,则该节点响应的指针属性为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的。
  4. 若某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  5. 对每一个节点,从该节点到其所有后代叶节点的简单路径(只能向下走,不能往回走)上,均包含相同数目的黑色节点

添加结点规则:添加的节点默认是红色(效率高)。

:直接变为黑色

非根

  • 父黑色:则不需要任何操作
  • 父红色
    • 叔叔红色:将“父”设为黑色,将“叔叔”设为黑色,将”祖父“设为”红色“。若祖父为根,则将根变回黑色;若祖父非根,将祖父设置为当前节点再进行其他判断
    • 叔叔黑色,当前节点是父的右孩子:把父作为当前节点并左旋(旋转时忽视Nil节点),再进行判断
    • 叔叔黑色,当前节点是父的左孩子:将”父“设为”黑色“,将”祖父“设为”红色“,以祖父为支点进行右旋

Collection

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

常用方法

add

public boolean add(E e)

把给定的对象添加到当前集合中

若向List系列集合中添加数据,由于List允许元素重复,永远返回true。

若向Map系列集合中添加数据,由于Map不允许元素重复,若当前添加元素不存在,返回true,表示返回成功;若当前添加元素存在,返回false,表示添加失败。

clear

public void clear()

清空集合中所有元素

remove

public boolean remove(E e)
删除集合中指定元素,若有,返回true,表示删除成功;若无,返回false,表示删除失败。
因为Collection中定义的是共性方法,List有索引,Map无索引,所以不能通过索引进行删除,只能通过元素对象进行删除。

contains

public boolean contains(Object obj)
判断当前集合中是否包含给定对象

:底层是依赖equals方法判断元素是否存在,若集合中存储的是定义对象,需在Javabean类中重写equals方法。

isEmpty

public boolean isEmpty()
判断当前集合是否为空

size

public int size()
判断集合中元素个数/集合的长度

注意事项

remove和contains底层依赖对象的equals方法。

范例
//Test.java
import java.util.ArrayList;
import java.util.Collection;
public class Test{
    public static void main(String[] args){
    	//以多态的形式创建集合,调用单列集合的公共方法
        Collection<String> collection=new ArrayList<>();
        System.out.println("add方法");
        collection.add("aaa");
        collection.add("bbb");
        collection.add("ccc");
        System.out.println(collection+"\n");

        System.out.println("clear方法");
        collection.clear();
        System.out.println(collection+"\n");

        System.out.println("remove方法");
        System.out.println(collection.remove("aaa"));
        collection.add("aaa");
        collection.add("bbb");
        collection.add("ccc");
        System.out.println(collection.remove("aaa"));
        System.out.println(collection+"\n");

        System.out.println("contains方法");
        System.out.println(collection.contains("bbb"));
        System.out.println(collection.contains("aaa"));
        Student student1=new Student(19,"张三");
        Collection<Student> collection1=new ArrayList<>();
        collection1.add(student1);
        Student student2=new Student(19,"张三");
        System.out.println(collection1.contains(student2)+"\n");

        System.out.println("isEmpty方法");
        System.out.println(collection.isEmpty()+"\n");

        System.out.println("size方法");
        System.out.println(collection);
        System.out.println(collection1);
        System.out.println(collection.size());
        System.out.println(collection1.size());

    }
}
//Student.java
import java.util.Objects;
public class Student {
    private int age;
    private String name;

    public Student(){}
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
     
     return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }
}

/*
add方法
[aaa, bbb, ccc]

clear方法
[]

remove方法
false
true
[bbb, ccc]

contains方法
true
false
true

isEmpty方法
false

size方法
[bbb, ccc]
[com.ljsblog.domain12.Student@14ae5a5]
2
1

*/

遍历方式

  • 迭代器遍历
  • 增强for遍历
  • Lambda表达式遍历
迭代器遍历

迭代器在Java之法的类是Iterator,迭代器是集合专用的遍历方式。
获取迭代器

  • Iterator iterator()
    • 返回迭代器对象,默认指向当前集合的0索引

常用方法

  • boolean hashNext()
    • 判断当前位置是否有元素,有元素,返回true;无元素,返回false
  • E next()
    • 获取当前位置的元素,并将迭代器对象移向下一个位置
    • 若当前位置没有更多的元素可供访问,会出现NoSuchElementException异常
    • 在循环过程中next方法最好只调用一次
  • default void remove()
    • 从基础集合中移除这个迭代器返回的最后一个元素(可选操作)。

范例

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test{
    public static void main(String[] args){
        Collection<String> collection=new ArrayList<>();
        collection.add("aaa");
        collection.add("bbb");
        collection.add("ccc");
        Iterator<String> iterator=collection.iterator();
        while(iterator.hasNext()){
            String str=iterator.next();
            System.out.println(str);
        }
    }
}

  1. 若当前位置没有元素,还要强行获取,则会NoSuchElementException
  2. 迭代器遍历完毕,指针不会复位,若想重新遍历,需创建新的迭代器对象
  3. 循环中只能用一次next方法,否则可能会越界
  4. 迭代器遍历时,不能用集合的方法进行增加或删除。若必须要删除,则可以用迭代器提供的remove方法进行删除。
增强for遍历

JDK5之后出现,内部原理就是一个Iterator迭代器,增强for的底层就是迭代器,为简化迭代器的代码书写的,所有的单列集合和数组才能用增强for进行遍历。

格式:

for(元素的数据类型 变量名:数组或单列集合){
}

:修改增强for中的变量,不会改变集合中原本的数据。

范例

import java.util.ArrayList;
import java.util.Collection;
public class Test{
    public static void main(String[] args){
        Collection<String> collection=new ArrayList<>();
        collection.add("aaa");
        collection.add("bbb");
        collection.add("ccc");
        for(String s:collection){
            System.out.println(s);
        }

    }
}
Lambda表达式遍历

方法名称
default void forEach(Consumer<? super T> action)
说明:结合lambda遍历集合
范例

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class Test{
    public static void main(String[] args){
        Collection<String> collection=new ArrayList<>();
        collection.add("aaa");
        collection.add("bbb");
        collection.add("ccc");
        //匿名内部类
        //底层原理:
        //遍历集合,得到每个元素
        //每个元素,传给accept方法。s依次表示集合中的每一个数据
        collection.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        //lambda省略版
        collection.forEach(s-> System.out.println(s));
    }
}

List

Collection的方法List全部继承,List集合因为有索引,所以多了很多索引操作的方法。

常用方法
add

void add(int index,E element)

在此集合的指定位置插入指定的元素

remove

格式1

E remove(int index)

删除指定索引处的元素,原来索引的上的元素会依次后移,返回被删除的元素

格式2

boolean remove(Object o)

直接删除元素

:在调用方法时,若方法出现重载现象,优先调用实参形参一致的方法

set

E set(int index,E element)

修改指定索引处的元素,返回被修改的元素

get

E get(int index)

返回指定索引处的元素

范例
import java.util.ArrayList;
import java.util.List;
public class Test{
    public static void main(String[] args){
        List<String> list=new ArrayList<>();
        System.out.println("add方法");
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        System.out.println(list+"\n");

        System.out.println("remove方法,通过索引删除");
        System.out.println(list.remove(1));
        System.out.println(list+"\n");
        System.out.println("remove方法,优先级判定");
        List<Integer> list1=new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        Integer i=1;
        //此处调用的是boolean remove(Object o)方法
        list1.remove(i);
        System.out.println(list1);
        List<Integer> list2=new ArrayList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        //此处调用的是E remove(int index)方法
        list2.remove(1);
        System.out.println(list2);

        System.out.println("set方法");
        System.out.println(list.set(1,"bbb"));
        System.out.println(list+"\n");

        System.out.println("get方法");
        System.out.println(list.get(1));
    }
}
/*
add方法
[aaa, bbb, ccc]

remove方法,通过索引删除
bbb
[aaa, ccc]

remove方法,优先级判定
[2, 3]
[1, 3]
set方法
ccc
[aaa, bbb]

get方法
bbb
*/
List集合遍历方式
  1. 迭代器遍历:在遍历的过程中需要删除元素,使用迭代器
    • 使用迭代器遍历集合的过程中,调用集合对象的添加,删除方法,就会出现并发修改异常ConcurrentModificaException
    • 迭代器遍历过程中,不允许使用集合的对象的添加或删除,那就使用迭代器自己的添加和删除方法
      • 迭代过程中删除:使用Iterator自带的remove方法
      • 迭代过程中添加:使用ListIterator自带的add方法
  2. 列表迭代器:ListIterator在遍历的过程中需要增加元素,使用列表迭代器
  3. 增强for遍历:仅仅想遍历,使用增强for或lambda表达式
  4. Lambda表达式
  5. 普通for:若遍历时想操作索引,可以使用普通for

范例

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Test{
    public static void main(String[] args){
        List<String> list=new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        //迭代器遍历
        Iterator<String> iterator=list.iterator();
        while (iterator.hasNext()){
            String str=iterator.next();
            if("ddd".equals(str)){
                iterator.remove();
            }
            System.out.println(str);
        }
        System.out.println(list+"\n");

        //增强for遍历
        for(String s:list){
            System.out.println(s);
        }
        System.out.println();

        //lambda表达式
        list.forEach(s-> System.out.println(s));
        System.out.println();

        //普通for
        for (int i = 0; i <list.size() ; i++) {
            System.out.println(list.get(i));
        }
        System.out.println();

        //列表迭代器
        ListIterator<String> listIterator=list.listIterator();
        while (listIterator.hasNext()){
            String str=listIterator.next();
            if("ccc".equals(str)){
                listIterator.add("ddd");
            }
            System.out.println(str);
        }
        System.out.println(list);
    }
}
/*
aaa
bbb
ccc
ddd
[aaa, bbb, ccc]

aaa
bbb
ccc

aaa
bbb
ccc

aaa
bbb
ccc

aaa
bbb
ccc
[aaa, bbb, ccc, ddd]
*/
ArrayList和LinkedList

ArrayList

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组。
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 数组存满时,会扩容1.5倍,若一次添加多个元素,1.5倍装不下,则新创建数组的长度以实际为为准。

LinkedList
底层数据结构是双链表,查询慢,增删快,若操作的是首尾元素,速度也是极快的。

LinkedList本身多出很多直接操作首尾元素的方法。

Set

Set接口中的方法基本上与Collection的API一致。

实现类

  • Hashset:无序、不重复、无索引
  • LinkedHashSet:有序,不重复,无索引
  • TreeSet:可排序,不重复,无索引

若要数据去重,默认使用HashSet,若要求去重且存取有序,使用LinkedHashSet。

范例

/*
利用Set系列的集合,添加字符串,并使用多种方式遍历
迭代器
增强for
lambda表达式
*/5
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test {
    public static void main(String[] args) {
        Set<String> s=new HashSet<>();
        System.out.println(s.add("aaa"));
        System.out.println(s.add("aaa"));//不可重复
        System.out.println(s.add("bbb"));
        System.out.println(s.add("ccc"));
        System.out.println(s);//打印无序

        System.out.println("\n"+"迭代器");
        Iterator<String> iterator=s.iterator();
        while(iterator.hasNext()){
            String s1=iterator.next();
            System.out.println(s1);
        }

        System.out.println("\n"+"for增强遍历");
        for(String s1:s){
            System.out.println(s1);
        }

        System.out.println("\n"+"lambda表达式");
        s.forEach(s1-> System.out.println(s1));

    }
}
/*
true
false
true
true
[aaa, ccc, bbb]

迭代器
aaa
ccc
bbb

for增强遍历
aaa
ccc
bbb

lambda表达式
aaa
ccc
bbb
*/
HashSet

HashSet集合底层采取哈希表存储数据

哈希表和哈希值

哈希表是一种对增删改查数据性能都较好的结构

哈希表组成

  • JDK8之前:数组+链表
  • JDK8开始:数组+链表+红黑树

哈希值

  • 根据hashCode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode的方法,利用对象内部的属性值计算哈希值

对象的哈希值的特点

  • 若并未重写hashCode方法,不同对象计算出的哈希值是不同的
  • 若并重写hashCode方法,不同对象只要属性值相同,计算出的哈希值是一样的
  • 在小部分情况下,不同的属性值或不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
//Test.java
public class Test {
    public static void main(String[] args) {
        Student student1=new Student("张三",18);
        Student student2=new Student("张三",18);
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
        //哈希碰撞
        System.out.println("abc".hashCode());
        System.out.println("acD".hashCode());
    }
}
//Student.java
import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
/*
24022538
24022538
96354
96354
*/
底层原理
  1. 创建一个默认长度16,默认加载因为0.75的数组,数组名为table。
    • 加载因即当数组存储元素为16*0.75=12时,会扩容一倍,数组长度变为32
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置i
    • int index=(数组长度-1)&哈希值;
  3. 判断当前位置是否为null,若是null直接存入
  4. 若位置不为null,表示有元素,则调用equals方法比较属性值
  5. 一样,不存;不同:存入数组形成链表
    • JDK8以前:新元素存入数组,老元素挂在新元素下面
    • JDK8以后:新元素直接挂在老元素下面

:jdk8以后,当链表长度超过8,且数组长度大于等于64时,自动转换为红黑树。

若集合中存储的是自定义对象,必须重写hashCode和equals方法。

范例
/*
需求:创建一个存储学生对象的集合,存储多个学生对象
使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
*/
//Student.java
import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
//Test.java
import java.util.HashSet;

public class Test {
    public static void main(String[] args) {
        Student student1=new Student("张三",18);
        Student student2=new Student("张三",18);
        Student student3=new Student("李四",18);
        Student student4=new Student("王五",19);
        HashSet<Student> hashSet=new HashSet<>();
        System.out.println(hashSet.add(student1));
        System.out.println(hashSet.add(student2));
        System.out.println(hashSet.add(student3));
        System.out.println(hashSet.add(student4));
        System.out.println(hashSet);

    }
}
/*
true
false
true
true
[Student{name = 王五, age = 19}, Student{name = 张三, age = 18}, Student{name = 李四, age = 18}]
*/
LinkedHashSet

有序不重复无索引

底层原理

底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存取的顺序。

范例
//Student.java

import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
//Test.java
import java.util.LinkedHashSet;

public class Test {
    public static void main(String[] args) {
        Student student1=new Student("张三",18);
        Student student2=new Student("张三",18);
        Student student3=new Student("李四",18);
        Student student4=new Student("王五",19);
        LinkedHashSet<Student> lhs=new LinkedHashSet<>();
        System.out.println(lhs.add(student1));
        System.out.println(lhs.add(student2));
        System.out.println(lhs.add(student3));
        System.out.println(lhs.add(student4));
        System.out.println(lhs);
    }
}
/*
true
false
true
true
[Student{name = 张三, age = 18}, Student{name = 李四, age = 18}, Student{name = 王五, age = 19}]
*/
TreeSet

不重复,无索引,可排序

可排序:按照元素的默认规则(从小到大)排序。

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都比较好。

范例

import java.util.Iterator;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet=new TreeSet<>();
        treeSet.add(99);
        treeSet.add(13);
        treeSet.add(123);
        treeSet.add(113);
        treeSet.add(69);
        treeSet.add(59);
        System.out.println(treeSet);
        System.out.println("\n迭代器遍历");
        Iterator<Integer> iterator=treeSet.iterator();
        while(iterator.hasNext()){
            int i=iterator.next();
            System.out.println(i);
        }

        System.out.println("\n增强for遍历");
        for(int n:treeSet){
            System.out.println(n);
        }

        System.out.println("\nlambda表达式");
        treeSet.forEach(i-> System.out.println(i));
    }
}
/*
[13, 59, 69, 99, 113, 123]

迭代器遍历
13
59
69
99
113
123

增强for遍历
13
59
69
99
113
123

lambda表达式
13
59
69
99
113
123
*/

TreeSet集合默认的规则

  • 对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
  • 对于字符,字符串类型,按照字符在ASCII码表中的数字升序进行排序
TreeSet的两种比较方式
  1. 默认排序/自然排序
  2. 比较器排序

使用原则:默认使用第一种,若第一种不能满足当前需求,则使用第二种。

当方式一和方式二重复时,以方式二为准。

默认排序/自然排序

JavaBean类实现Comparable接口,指定比较规则。

范例

/*
需求:创建TreeSet集合,并添加4个学生对象
学生对象属性:
	姓名,年龄
	要求按照学生的年龄进行排序,同年龄按照字母排序
	同姓名,同年龄认为是同一个人
*/
//Student.java
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student o) {
        //this:表示当前要添加的元素
        //o:表示已在红黑树存在的元素
        //返回值:
        //负数:表示当前要添加的元素是小的,存左边
        //正数:表示当前要添加的元素是大的,存右边
        //0:表示当前添加的元素已存在,舍弃
        int i=this.getAge()-o.getAge();
        return i==0?this.getName().compareTo(o.getName()):i;
    }
}

//Test.java
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<Student> treeSet=new TreeSet<>();
        Student student1=new Student("zs",23);
        Student student2=new Student("ls",19);
        Student student3=new Student("bw",19);
        Student student4=new Student("zz",25);
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        System.out.println(treeSet);

    }
}
/*
[Student{name = bw, age = 19}, Student{name = ls, age = 19}, Student{name = zs, age = 23}, Student{name = zz, age = 25}]
*/
比较器排序

创建TreeSet对象时,传送比较器Comparator指定规则。

/*
要求:存入四个字符串,"asadsa""dasddw""rgreg""feg",按照长度排序,若等长则按照首字母排序
*/
//import java.util.Comparator;
import java.util.TreeSet;
public class Test {
    public static void main(String[] args) {
        /*
        TreeSet<String> treeSet=new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int i = o1.length()-o2.length();
                return i==0?o1.compareTo(o2):i;
            }
        });
        */
        //o1:表示当前要添加的元素
        //o2:表示已在红黑树中存在的元素
        //返回值规则跟之前一样
        TreeSet<String> treeSet=new TreeSet<>((o1,o2)->{
            int i = o1.length()-o2.length();
            return i==0?o1.compareTo(o2):i;
        });
        treeSet.add("asadsa");
        treeSet.add("dasddw");
        treeSet.add("rgreg");
        treeSet.add("feg");
        System.out.println(treeSet);
    }
}
//[feg, rgreg, asadsa, dasddw]
范例
/*
创建5个学生对象
属性:姓名,年龄,语文成绩,数学成绩,英语成绩
按照总分从高到低输出
若总分一样,按语文排
若语文一样,按数学排
若数学一样,按英语排
若英语一样,按年龄排
若年龄一样,按姓名排
若全部一样,认为是同一学生,不存
*/
//Student.java
package com.ljsblog.domain1;

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private int chinese_score;
    private int math_score;
    private int english_score;

    public Student() {
    }

    public Student(String name, int age, int chinese_score, int math_score, int english_score) {
        this.name = name;
        this.age = age;
        this.chinese_score = chinese_score;
        this.math_score = math_score;
        this.english_score = english_score;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return chinese_score
     */
    public int getChinese_score() {
        return chinese_score;
    }

    /**
     * 设置
     * @param chinese_score
     */
    public void setChinese_score(int chinese_score) {
        this.chinese_score = chinese_score;
    }

    /**
     * 获取
     * @return math_score
     */
    public int getMath_score() {
        return math_score;
    }

    /**
     * 设置
     * @param math_score
     */
    public void setMath_score(int math_score) {
        this.math_score = math_score;
    }

    /**
     * 获取
     * @return english_score
     */
    public int getEnglish_score() {
        return english_score;
    }

    /**
     * 设置
     * @param english_score
     */
    public void setEnglish_score(int english_score) {
        this.english_score = english_score;
    }

    public int getSum(){
        return chinese_score+math_score+english_score;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", chinese_score = " + chinese_score + ", math_score = " + math_score + ", english_score = " + english_score + ", sum = " + getSum() + "}";
    }

    @Override
    public int compareTo(Student o) {
        if(o.getSum()!=this.getSum()){
            return o.getSum()-this.getSum();
        }else if(o.getChinese_score()!=this.getChinese_score()){
            return o.getChinese_score()-this.getChinese_score();
        }else if(o.getMath_score()!=this.getMath_score()){
            return o.getMath_score()-this.getMath_score();
        }else if(o.getEnglish_score()!=this.getEnglish_score()){
            //因为总分相等,语文相等,数学相等,英语必然相等,这里的英语可以不写,但还是写上了
            return o.getEnglish_score()-this.getEnglish_score();
        }else if(o.getAge()!=this.getAge()){
            return o.getAge()-this.getAge();
        }else{
            return o.getName().compareTo(this.getName());
        }
    }
}
//Test.java
import java.util.TreeSet;
public class Test {
    public static void main(String[] args) {
        TreeSet<Student> treeSet=new TreeSet<>();
        Student student1=new Student("one",19,88,100,99);
        Student student2=new Student("two",19,88,100,99);
        Student student3=new Student("three",20,76,26,48);
        Student student4=new Student("four",23,97,46,18);
        Student student5=new Student("five",18,65,95,26);
        Student student6=new Student("one",19,88,100,99);
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        treeSet.add(student5);
        treeSet.add(student6);
        for(Student student:treeSet){
            System.out.println(student);
        }
    }
}
/*
Student{name = two, age = 19, chinese_score = 88, math_score = 100, english_score = 99, sum = 287}
Student{name = one, age = 19, chinese_score = 88, math_score = 100, english_score = 99, sum = 287}
Student{name = five, age = 18, chinese_score = 65, math_score = 95, english_score = 26, sum = 186}
Student{name = four, age = 23, chinese_score = 97, math_score = 46, english_score = 18, sum = 161}
Student{name = three, age = 20, chinese_score = 76, math_score = 26, english_score = 48, sum = 150}
*/

使用场景

  1. 若想集合中元素可重复
    • 用ArrayList集合,基于数组的(用的最多)
  2. 若想集合中元素可重复,且当前的增删操作明显多于查询
    • 用LinkedList集合,基于链表的
  3. 若想对集合中元素去重
    • 用HashSet集合,基于哈希表的(用的最多)
  4. 若想对集合中元素去重,而且保证存取顺序
    • 用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet。
  5. 若想对集合中的元素进行排序
    • 用TreeSet集合,基于红黑树,后续也可用List集合实现排序

Map

双列集合特点

  1. 双列集合一次需存储一对数据,分别为
  2. 不能重复,可以重复
  3. 是一一对应的,每个键只能找到自己对应的值
  4. 这个整体称之为键值对键值对对象,在Java中叫做Entry对象

Map是双列集合的顶层接口,它的功能是全部双列集合都可继承使用的。

体系结构

  • Map(接口)
    • HashMap (实现类)->LinkedHashMap(实现类)
    • Hashtable(实现类)->Properties(实现类)
    • TreeMap(实现类)

常用方法

put

格式

V put(K key,V value)

说明:添加元素。

  • 在添加数据时,若键不存在,直接把键值对对象添加到map集合当中,方法返回是null
  • 在添加数据时,若键存在,会把原有的键值对对象覆盖,并将被覆盖的值进行返回。
remove

格式

V remove(Object Key)

说明:根据键删除键值对元素

clear

格式

void clear()

说明:移除所有键值对元素

containsKey

格式

boolean containsKey

说明:判断集合是否包含指定的键

containsValue

格式

boolean containsValue

说明:判断集合是否包含指定的值

isEmpty

格式

boolean isEmpty()

说明:判断集合是否为空

size

格式

int size()

说明:集合的长度,也是集合中键值对的个数

范例

import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) {
        Map<String,String> m=new HashMap<>();
        System.out.println("put方法");
        m.put("aaa","AAA");
        m.put("bbb","BBB");
        m.put("ccc","CCC");
        System.out.println(m);
        String value1=m.put("aaa","sss");
        System.out.println(m);
        System.out.println(value1);
        String value2=m.put("ddd","DDD");
        System.out.println(value2);
        System.out.println(m);

        System.out.println("\nremove方法");
        m.remove("bbb");
        System.out.println(m);

        System.out.println("\ncontainsKey方法");
        System.out.println(m.containsKey("aaa"));
        System.out.println(m.containsKey("bbb"));


        System.out.println("\ncontainsValue方法");
        System.out.println(m.containsValue("DDD"));
        System.out.println(m.containsValue("AAA"));


        System.out.println("\nsize方法");
        System.out.println(m.size());

        System.out.println("\nisEmpty方法");
        System.out.println(m.isEmpty());

        System.out.println("\nclear方法");
        m.clear();
        System.out.println(m.isEmpty());
        System.out.println(m);
    }
}

遍历方式

  1. 键找值
  2. 键值对
  3. Lambda表达式
键找值

利用keySet方法将所有键放入一个单列集合中,遍历得到每一个键,通过get方法得到每个键对应的值。

keySet方法

Set keySet()

get方法

V get(Object key)

范例

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
    public static void main(String[] args) {
        Map<String,String> m=new HashMap<>();
        m.put("aaa","AAA");
        m.put("bbb","BBB");
        m.put("ccc","CCC");

        Set<String> set=m.keySet();
        System.out.println("迭代器遍历");
        Iterator<String> it=set.iterator();
        while(it.hasNext()){
            String str=it.next();
            System.out.println(str+" = "+m.get(str));
        }

        System.out.println("\n增强for");
        for(String str:set){
            System.out.println(str+" = "+m.get(str));
        }

        System.out.println("\nlambda");
        set.forEach(str-> System.out.println(str+" = "+m.get(str)));
    }
}
/*
迭代器遍历
aaa = AAA
ccc = CCC
bbb = BBB

增强for
aaa = AAA
ccc = CCC
bbb = BBB

lambda
aaa = AAA
ccc = CCC
bbb = BBB
*/
键值对

通过entrySet方法获取所有键值对对象,在通过键值对对象调用getkeygetValue获取键和值。

entrySet方法

Set<Map.Entry<K,V>> entrySet()

getkey方法

K getKey()

getValue方法:

V getValue()

范例

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
    public static void main(String[] args) {
        Map<String,Integer> map=new HashMap<>();
        map.put("a",97);
        map.put("b",98);
        map.put("c",99);
        Set<Map.Entry<String,Integer>> entries=map.entrySet();
        //若导入import java.util.Map.Entry;
        //则可以直接Set<Entry<String,Integer>> entries=map.entrySet();
        System.out.println("迭代器");
        Iterator<Map.Entry<String,Integer>> it=entries.iterator();
        while(it.hasNext()){
            Map.Entry<String,Integer> entry=it.next();
            String key=entry.getKey();
            int value=entry.getValue();
            System.out.println(key+" = "+value);
        }

        System.out.println("\nfor增强");
        for(Map.Entry<String,Integer> entry:entries){
            String key=entry.getKey();
            int value=entry.getValue();
            System.out.println(key+" = "+value);
        }

        System.out.println("\nLambda");
        entries.forEach(entry-> System.out.println(entry.getKey()+" = "+entry.getValue()));
    }
}
/*
迭代器
a = 97
b = 98
c = 99

for增强
a = 97
b = 98
c = 99

Lambda
a = 97
b = 98
c = 99
*/
Lambda表达式

方法名称

default void forEach(BiConsumer<? super K, ? super V> action)

说明:结合Lambda遍历Map集合

范例

import java.util.HashMap;
import java.util.Map;
//import java.util.function.BiConsumer;
public class Test{
    public static void main(String[] args){
        Map<String,Integer> map=new HashMap<>();
        map.put("a",97);
        map.put("b",98);
        map.put("c",99);
        /*
        map.forEach(new BiConsumer<String, Integer>() {
            @Override
            public void accept(String s, Integer integer) {
                System.out.println(s+" = "+integer);
            }
        });
        */
        map.forEach((s,integer)-> System.out.println(s+" = "+integer));
    }
}
/*
a = 97
b = 98
c = 99
*/

选择方式

默认:HashMap(效率最高)

若要保证存取有序,LinkedHashMap

若要进行排序:TreeMap

HashMap

  1. HashMap是Map里的一个实现类,并无额外需要学习的特殊方法,直接使用Map的方法即可
  2. 特点是由键决定:无序,不重复,无索引
  3. HashMap和HashSet底层原理一样,都是哈希表结构
  4. 依赖hashCode和equals方法保证键的唯一
  5. 若键存储的是自定义对象,需重写hashCode和equals方法;若值存储的是自定义对象,不需重写hashCode和equals方法。
范例一
/*
创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)
存储三个键值对元素,并遍历。
要求:同姓名,同年龄认为是同一个学生
*/
//Student.java
import java.util.Objects;

public class Student{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
//Test.java
import java.util.HashMap;
import java.util.Set;
public class Test {
    public static void main(String[] args) {
        HashMap<Student,String> hm=new HashMap<>();
        Student stu1=new Student("zs",18);
        Student stu2=new Student("lisi",19);
        Student stu3=new Student("zs",18);
        String s1="河北";
        String s2="河南";
        String s3="北京";
        hm.put(stu1,s1);
        hm.put(stu2,s2);
        hm.put(stu3,s3);

        System.out.println("lambda");
        hm.forEach((stu,s)-> System.out.println(stu+" = "+ s));

        System.out.println("键找值");
        Set<Student> set=hm.keySet();
        for(Student stu:set){
            System.out.println(stu +" = "+hm.get(stu));
        }
        System.out.println("键值对");
        Set<HashMap.Entry<Student,String>> entries=hm.entrySet();
        for(HashMap.Entry<Student,String> entry:entries){
            System.out.println(entry.getKey()+ " = "+entry.getValue());
        }
    }
}
/*
lambda
Student{name = lisi, age = 19} = 河南
Student{name = zs, age = 18} = 北京
键找值
Student{name = lisi, age = 19} = 河南
Student{name = zs, age = 18} = 北京
键值对
Student{name = lisi, age = 19} = 河南
Student{name = zs, age = 18} = 北京
*/
范例二
/*
某个班级80个学生,现需组成春游活动
班长提供四个景区A,B,C,D
每个学生只能选择一个景区,并统计出哪个景区想去的人数最多
*/
package com.ljsblog.domain1;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
        String[] arr={"A","B","C","D"};
        ArrayList<String> list=new ArrayList<>();
        Random random=new Random();
        for(int i=0;i<80;i++){
            int index=random.nextInt(arr.length);
            list.add(arr[index]);
        }
        HashMap<String,Integer> hm=new HashMap<>();
        for(String name:list){
            if(hm.containsKey(name)){
                int c=hm.get(name);
                c++;
                hm.put(name,c);
            }else{
                hm.put(name,1);
            }
        }
        Set<HashMap.Entry<String,Integer>> entries=hm.entrySet();
        int max=0;
        for(HashMap.Entry<String,Integer> entry:entries){
            int value=entry.getValue();
            if(max<value){
                max=value;
            }
        }
        System.out.println(hm);
        System.out.println("最大为"+max);
        for(HashMap.Entry<String,Integer> entry:entries){
            if(max==entry.getValue()){
                System.out.println(entry.getKey());
            }
        }

    }
}
/*
{A=19, B=18, C=21, D=22}
最大为22
D
*/

LinkedHashMap

由键决定:有序不重复无索引

底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存取的顺序。

范例

import java.util.LinkedHashMap;
public class Test {
    public static void main(String[] args) {
        LinkedHashMap<String ,Integer> lhm=new LinkedHashMap<>();
        lhm.put("a",96);
        lhm.put("a",97);
        lhm.put("b",98);
        lhm.put("c",99);
        System.out.println(lhm);
    }
}
/*
{a=97, b=98, c=99}
*/

TreeMap

TreeMap跟TreeSet底层原理一样,都是红黑树结构的。

由键决定特性:不重复,无索引,可排序。

可排序:对键进行排序、

:默认按照键的从小到大进行排序,也可自己规定键的排序规则

代码书写的两种排序规则

  1. 实现Comparable接口,指定比较规则。
  2. 创建集合时传递Comparator比较器对象,指定比较规则
范例一
/*
键:整数表示id
值:字符串表示商品名称
要求:按照id的升序排序,按照id的降序排序
*/

import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        //默认升序
        TreeMap<Integer,String> tm1=new TreeMap<>();
        tm1.put(6,"足球");
        tm1.put(5,"篮球");
        tm1.put(10,"橄榄球");
        System.out.println(tm1);

        //降序
        TreeMap<Integer,String> tm2=new TreeMap<>((o1, o2)->o2-o1);
        tm2.put(7,"足球");
        tm2.put(6,"篮球");
        tm2.put(10,"橄榄球");
        System.out.println(tm2);
    }
}
范例二
/*
键:学生对象(姓名和年龄)
值:籍贯
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排序,同姓名通年龄视为同一人
*/
//Student.java
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student o) {
        int i=this.getAge()-o.getAge();
        return i==0?this.getName().compareTo(o.getName()):i;
    }
}
//Test.java
import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        TreeMap<Student,String> tm=new TreeMap<>();
        Student s1=new Student("zs",19);
        Student s2=new Student("ww",18);
        Student s3=new Student("zs",19);
        Student s4=new Student("ls",19);
        tm.put(s1,"河北");
        tm.put(s2,"河南");
        tm.put(s3,"湖南");
        tm.put(s4,"湖北");
        System.out.println(tm);
    }
}
范例三
/*
统计字符串"aababcabcdabcde"每个字符出现的次数,并按以下格式输出:
a(5)b(4)c(3)d(2)e(1)
*/
import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        String str="aababcabcdabcde";
        TreeMap<Character,Integer> tm=new TreeMap<>();
        for(int i=0;i<str.length();i++){
            Character c=str.charAt(i);
            if(tm.containsKey(c)){
                int count=tm.get(c);
                count++;
                tm.put(c,count);
            }else {
                tm.put(c,1);
            }
        }
        StringBuilder sb=new StringBuilder();
        tm.forEach((character, integer)->sb.append(character).append("(").append(integer).append(")"));
        System.out.println(sb);
    }
}
/*
a(5)b(4)c(3)d(2)e(1)
*/

可变参数

可变参数本质上是一个数组。

作用:在形参中接收多个数据。

格式

数据类型…参数名称

注意事项

  • 形参列表中可变参数只有一个
  • 可变参数必须放在形参列表的最后面

范例

public class Test {
    public static void main(String[] args) {
        System.out.println(sum(1, 2, 3, 4));
        System.out.println(sum(1, 2, 3, 4, 5));
        System.out.println("大于7的数字有"+compare(7, 11, 9, 4, 6, 7)+"个");
    }
    public static int sum(int...args){
        int s=0;
        for (int num:args){
            s+=num;
        }
        return s;
    }
    public static int compare(int num,int...args){
        int c=0;
        for(int i=0;i<args.length;i++){
            if(args[i]>num){
                c++;
            }
        }
        return c;
    }
}
/*
10
15
大于7的数字有2个
*/

Collections

java.util.Collections是集合工具类。
作用:Collections不是集合,而是集合的工具类

常用方法

addAll

格式
public static boolean addAll(Collection c,T…elements)
说明
批量添加元素

shuffle

格式
public static void shuffle(List<?> list)
说明
打乱List集合元素的顺序

范例

import java.util.ArrayList;
import java.util.Collections;
public class Test {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        System.out.println("addAll方法");
        Collections.addAll(list, "saf","fasf","df","fsa");
        System.out.println(list+"\n");

        System.out.println("shuffle方法");
        Collections.shuffle(list);
        System.out.println(list);
    }
}
/*
addAll方法
[saf, fasf, df, fsa]

shuffle方法
[saf, df, fsa, fasf]
*/

范例

例1

/*
班级里有一些学生。
要求:被点到的学生不会再被点到。
若班级中所有学生点完,则开始下一轮点名
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        Collections.addAll(list,"张三","李四","王五","甲","乙","丙","丁");
        ArrayList<String> list1=new ArrayList<>();
        int i=1;
        Scanner s=new Scanner(System.in);
        Random r=new Random();
        while(true){
            System.out.print("若想退出输入0,非零继续循环,请输入:");
            String in=s.nextLine();
            if(in.compareTo("0")==0){
                break;
            }
            System.out.println("第"+i+"轮点名");
            list1.addAll(list);
            while(list1.size()!=0){
                int num=r.nextInt(list1.size());
                System.out.println(list1.get(num));
                list1.remove(num);
            }
            list1.clear();
            i++;
        }
    }
}

例2

/*
定义一个map集合,键用表示省份名称province,值表示市city,但是市有多个,添加完毕后,遍历结果格式如下:
江苏省=南京市,扬州市,苏州市
湖北省=武汉市,孝感市,宜昌市
河北省=唐山市,邢台市,廊坊市
*/
import java.util.*;
public class Test {
    public static void main(String[] args) {
        ArrayList<String> city1=new ArrayList<>();
        city1.add("南京市");
        city1.add("扬州市");
        city1.add("苏州市");

        ArrayList<String> city2=new ArrayList<>();
        city2.add("武汉市");
        city2.add("孝感市");
        city2.add("宜昌市");

        ArrayList<String> city3=new ArrayList<>();
        city3.add("唐山市");
        city3.add("邢台市");
        city3.add("廊坊市");

        HashMap<String,ArrayList<String>> m=new HashMap<>();
        m.put("江苏省",city1);
        m.put("湖北省",city2);
        m.put("河北省",city3);

        Set<Map.Entry<String,ArrayList<String>>> entries=m.entrySet();
        for(Map.Entry<String,ArrayList<String>> entry:entries){
            StringJoiner sj=new StringJoiner(",");
            ArrayList<String> value = entry.getValue();
            for(String s:value){
                sj.add(s);
            }
            System.out.println(entry.getKey()+"="+sj);
        }
    }
}
/*
江苏省=南京市,扬州市,苏州市
湖北省=武汉市,孝感市,宜昌市
河北省=唐山市,邢台市,廊坊市
*/

不可变集合

不可变集合:集合不可变,不能添加,修改,删除。

格式

在List,Set,Map接口中,均存在静态of方法,可获得一个不可变的集合。

  • 方法:static List of(E…element)
    • 创建一个具有指定元素的List集合对象
  • 方法:static Set of(E…element)
    • 创建一个具有指定元素的set集合对象,元素不能重复
  • 方法:static <K,V> Map<K,V> of(E…element)
    • 创建一个具有指定元素的Map集合对象,元素不能重复,键值对数量最多10个,超过10个用ofEntries方法

Properties

properties是一个双列集合,拥有Map集合的所有特点,本身也有一些特有方法,可把集合的数据按照键值对的形式写到配置文件(后缀.properties)当中,也可把配置文件中的数据,读取到集合中。

propertise集合没有泛型,可以添加任意类型数据,但一般添加字符串类型数据。

常用方法

store

格式1

public void store(Writer writer,String comments)

格式2

public void store(OutputStream out,String comments)

说明:把集合中的数据以键值对的形式写入到文件中,第二个参数为写入的注释

load

格式1

public void load(Reader reader)

格式2

public void load(InputStream inStream)

说明:读取本地文件里的数据。

范例
import java.io.*;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws IOException {
        Properties p1=new Properties();
        p1.put("user","zhangsan");
        p1.put("password","123456");
        BufferedWriter bw=new BufferedWriter(new FileWriter("a.properties"));
        p1.store(bw,"用户和密码");
        bw.close();

        Properties p2=new Properties();
        BufferedReader br=new BufferedReader(new FileReader("a.properties"));
        p2.load(br);
        System.out.println(p2);
    }
}
/*a.properties内容
#用户和密码
#Tue Apr 04 17:00:17 CST 2023
password=123456
user=zhangsan

*/

/*打印内容
{password=123456, user=zhangsan}
*/
  • 31
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值