java集合

数组和集合

数组的特点

  • 数组中保存的元素都是有序的,可以通过下标快速访问
  • 数组中保存的数据都是同一种类型
  • 数组的长度在定义后,无法改变
  • 数组无法获取其中保存的元素实际数量

集合的特点

  • 能保存一组数据,可以有序可以无序
  • 集合的容量可变
  • 集合中可以保存不同类型的数据
  • 可以获取集合中保存的元素实际数量

集合框架(集合家族)

Collection接口

该接口有两个核心子接口:List和Set。

这两个接口都可以保存一组元素,List接口保存元素时,是有序可重复的;Set接口保存元素时,是无序不重复的。

常用方法返回值作用
add(Object obj)boolean将元素添加到集合中
size()int获取集合中的元素数量
isEmpty()boolean判断集合是否为空
clear()void清空集合
contains(Object obj)boolean判断集合中是否存在指定元素
remove(Object obj)boolean移除集合中的指定元素
toArray()Object[]将集合转换为数组
iterator()Iterator获取集合的迭代器对象,用于遍历集合

List接口(有序可重复)

有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。

在该接口继承Collection接口的同时,又拓展了一些操作元素的方法,如添加到指定索引、根据索引删除、获取指定索引的元素、截取子集合的方法等。

常用方法返回值作用
get(int index)Object根据指定索引获取对应的元素
set(int index,Object obj)Object使用obj替换index上的元素,返回被替换的元素
add(int index,Object obj)void将obj添加到index上
remove(int index)Object移除指定索引的元素
indexOf(Object obj)int得到某元素第一次出现的索引,没有返回-1
lastIndexOf(Object obj)int得到某元素最后一次出现的索引,没有返回-1
subList(int from,int to)List截取[from,to)区间内的元素,返回子集合

ArrayList实现类(掌握)

  • 采用数组实现的集合
  • 可以通过索引访问元素,可以改变集合大小。如果要在其中插入或删除元素时,会影响后续元素
  • 该集合中保存的都是引用类型,即便保存了数组123,也保存的是Integer类型的123,而不是int类型的123
  • 该集合查询效率高,中途增加和删除元素效率低
构造方法
常用构造方法说明
ArrayList()创建一个Object类型的空数组。在调用添加方法后,才会更改该数组大小为10
ArrayList(int initialCapacity)创建一个指定容量的Object数组,如果参数为负,会抛出IllegalArgumentException异常
常用方法

ArrayList中的常用方法,就是Collection接口和List接口中定义的方法。

LinkedList实现类

  • 采用双向链表实现的集合
  • 集合中保存的每个元素也称为节点,除首尾节点外,其余节点都保存了自己的信息外,还保存了其前一个和后一个节点的地址
  • 如果在双向链表的数据结构中插入和删除操作节点时,不会影响其他节点的位置。如添加时新节点时,只需要重写定义新节点的前后节点位置即可
  • 如果要查询某个节点时,需要从头结点或尾结点开始一步步得到目标节点的位置
  • 双向链表在中间插入和删除的效率高,随机读取的效率低

img

构造方法
常用构造方法说明
LinkedList()创建一个空链表
常用方法

由于LinkedList既实现了List接口,又实现了Deque接口,所以还有Deque接口中的一些方法

实现Deque接口的方法说明
addFirst(Object obj)添加头元素
addLast(Object obj)添加尾元素
removeFirst()移除头元素
removeLast()移除尾元素
getFirst()得到头元素
getLast()得到尾元素
remove()移除头元素
pop()移除头元素
push(Object obj)添加头元素
peek()得到头元素
poll()移除头元素
offer(Object obj)添加尾元素

ArrayList和LinkedList的区别

  • 这两个类都是List接口的实现类,保存的元素有序可重复,允许保存null
  • ArrayList采用数组实现,随机读取效率高,插入删除效率低,适合用于查询
  • LinkedList采用双向链表实现,插入删除时不影响其他元素,效率高,随机读取效率低,适合用于频繁更新集合

Set接口(无序不重复)

无序集合,元素不可以重复,允许保存null,没有索引。

Set接口中没有自己定义的方法,都是继承于Collection接口中的方法

哈希表hash table

哈希表,也称为散列表,是一种数据结构,能更快地访问数据。

要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构造一张映射表,这个表称为哈希表。在哈希表中,可以通过哈希码快速地访问对应的原始值。

假设原本的数据为左侧的数组。

如果要查询10,需要遍历数组,效率不高。

通过一个特定的函数"原始值%5",得到一组新数据,让新数据重新对应元素,保存到“新数组”中,这个“新数组”称为哈希表。

这时如果要查询10,由于哈希函数是通过%5得到了0,所以直接查询哈希表中0对应的元素即可。

整个过程中,这个函数称为哈希函数,得到的新数据称为哈希码,新数组称为哈希表,对应关系称为哈希映射。

这个哈希函数,有一定的几率让多个原始值得到相同的哈希码,这种情况称为哈希冲突(哈希码一致,实际值不同),

为了解决哈希冲突,可以使用"拉链法",将2这个哈希码所在的位置向链表一样进行延伸。

哈希码的特点

  • 如果两个对象的hashCode不同,这两个对象一定不同
  • 如果两个对象的hashCode相同,这两个对象不一定相同
    • hashCode相同,对象不同,这种现象称为哈希冲突
    • **“通话""重地”**这两个字符串的hashCode相同,但是两个不同的对象

HashSet实现类

  • 采用哈希表实现
  • 元素不能重复,无序保存,允许保存一个null
  • 本质是一个HashMap对象
  • 使用HashSet集合时,通常要重写实体类中的equals和hashcode方法
构造方法
常用构造方法说明
HashSet()创建一个空集合,实际是创建一个HashMap对象。
常用方法

HashSet中没有属于自定义的方法,都是重写了父接口Set和Collection中的方法。这里参考Collection中的方法即可。

没有与索引相关的方法。

HashSet添加数据的原理

如果两个元素的hashCode相同且equals结果为true,视为同一个对象,不能添加。

每次向集合中添加元素时,先判断该元素的hashCode是否存在

  • 如果不存在,视为不同对象,直接添加
  • 如果存在,再判断equals方法的结果
    • 如果false,视为不同对象,可以添加
    • 如果true,视为同一对象,不能添加

由此可见,不能添加的条件是两个对象的hashCode相同且equals的结果为true。

如果每次只判断equals的话,由于equals方法通常重写时会判断很多属性,效率不高。

如果每次只判断hashCode的话,效率高,但有可能会有哈希冲突,

所以先判断hashCode,再判断equals,技能保证效率,又能保证不添加重复元素。

equals方法和hashCode的关系
  • 如果两个对象的equals方法结果为true,在没有重写equals方法的前提下,hashCode相同吗

    • 如果没有重写equals,默认是Object中使用==判断,如果结果为true,说明是同一个对象,hashCode一定相同
  • 如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法的结果为?

    • hashCode不同,说明不是同一个对象,没有重写equals,说明使用Object中equals的==判断,结果为false
  • 如果两个对象的hashCode相同,equals方法的比较结果为?

    • 可能为true也可能为false

      String str1="hello";
      String str2="hello";
      //以上两个字符串使用同一个地址,hashCode相同,equals方法为true
      
      String str3="通话";
      String str4="重地";
      //以上连个字符串是不同地址,但hashCode相同,因为哈希冲突,equals方法为false
      
HashSet的应用

如果想要保存的对象保证不重复,且无关顺序,可以使用HashSet。如学生管理

Goods类

package com.hqyj.hashsetTest;

import java.util.Objects;

/*
 * 定义商品类
 * 品牌、名称、价格
 * */
public class Goods {
    private String brand;
    private String name;
    private int price;


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Goods goods = (Goods) o;
        return price == goods.price &&
                Objects.equals(brand, goods.brand) &&
                Objects.equals(name, goods.name);
    }

    /*
    * 根据所有属性生成哈希码
    * 如果两个对象的所有属性都一致,生成的哈希码就一致
    * */
    @Override
    public int hashCode() {
        return Objects.hash(brand, name, price);
    }

    @Override
    public String toString() {
        return "Goods{" +
                "brand='" + brand + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    public Goods(String brand, String name, int price) {
        this.brand = brand;
        this.name = name;
        this.price = price;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Main类

package com.hqyj.hashsetTest;

import java.util.HashSet;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Goods> hs = new HashSet<>();
        //创建几个Goods对象
        //g1、g2、g3的属性不同,生成的hashcode不同,都能添加
        Goods g1 = new Goods("康师傅", "冰红茶", 3);
        Goods g2 = new Goods("康师傅", "红烧牛肉面", 5);
        Goods g3 = new Goods("农夫山泉", "矿物质水", 2);
        //g3与g4的属性相同,生成的hashcode相同,继续判断equals
        Goods g4 = new Goods("农夫山泉", "矿物质水", 2);

        //第一次添加,一定可以添加
        hs.add(g1);
        //第二次添加,对象的属性不同,hashcode不同,可以添加
        hs.add(g2);
        //第三次添加,对象的属性不同,hashcode不同,可以添加
        hs.add(g3);
        //第四次添加,对象的属性相同,hashcode相同,再判断equals结果,true,视为已存在,无法添加
        hs.add(g4);

        /*
         * HashSet没有可以通过索引获取对象的方法,所以无法使用普通for循环遍历
         * 这里可以使用增强for循环
         * */
        for (Goods g : hs) {
            System.out.println(g);
        }

        /*
         * 可以使用迭代器遍历HashSet集合中的所有元素
         * */
        /*Iterator<Goods> it = hs.iterator();
        while (it.hasNext()) {
            Goods goods = it.next();
            System.out.println(goods);
        }*/

    }
}

TreeSet实现类

  • 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null
  • 采用红黑树(自平衡二叉树)实现的集合
    • 二叉树表示某个节点最多有两个子节点
    • 某个节点右侧节点值都大于左侧节点值
    • 红黑树会经过不停的"变色"、"旋转"达到二叉树的平衡
  • 只能添加同一种类型的对象且该类实现了Comparable接口
    • 实现Comparable接口后必须要重写compareTo()方法
    • 每次调用添加add(Object obj)方法时,就会自动调用参数的compareTo()方法
  • compareTo()方法的返回值决定了能否添加新元素和新元素的位置
    • 如果返回0,视为每次添加的是同一个元素,不能重复添加
    • 如果返回正数,将新元素添加到现有元素之后
    • 如果返回负数,将新元素添加到现有元素之前
  • 添加的元素可以自动排序
构造方法
常用构造方法说明
TreeSet()创建一个空集合,实际是创建了一个TreeMap对象
常用方法

属于Set的实现类,所以能使用Collection和Set中的方法,除此之外,还有独有的方法

常用方法作用
fisrt()得到集合中的第一个元素
last()得到集合中的最后一个元素
ceil(Object obj)得到比指定元素obj大的元素中的最小元素
floor(Object obj)得到比指定元素obj小的元素中的最大元素
TreeSet的应用

如果要保存的元素需要对其排序,使用该集合。

保存在其中的元素必须要实现Comparable接口,且重写compareTo()方法,自定义排序规则

Employee类

package com.hqyj.TreeSetTest;

import java.util.Objects;

/*
 * 定义员工类
 * 编号
 * 姓名
 * 部门
 * */
public class Employee implements Comparable {
    private int no;
    private String name;
    private String dept;


    @Override
    public String toString() {
        return "Employee{" +
                "no='" + no + '\'' +
                ", name='" + name + '\'' +
                ", dept='" + dept + '\'' +
                '}';
    }

    public Employee(int no, String name, String dept) {
        this.no = no;
        this.name = name;
        this.dept = dept;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

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

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    @Override
    public int compareTo(Object o) {
        //此时this是当前添加的对象
        //o是已存在的集合中的对象
        Employee emp = (Employee) o;
        //这里用当前员工号-已有员工号,根据员工号升序
        return this.getNo()-emp.getNo();
        //根据姓名字符串的排序方式排序
        // return emp.getName().compareTo(this.getName());
    }
}


Main类

package com.hqyj.TreeSetTest;

import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {


        TreeSet<Employee> emps = new TreeSet<>();

        Employee e1 = new Employee(10023,"aaa","市场部");
        Employee e2 = new Employee(10025,"ccc","市场部");
        Employee e3 = new Employee(10028,"bbb","市场部");
        Employee e4 = new Employee(10028,"xxx","市场部");

        //第一个元素直接添加
        emps.add(e1);
        //第二个元素添加时,调用compareTo()方法  e2.compareTo(e1)  e2.10025 - e1.10023 结果为正,添加在现有元素之后
        emps.add(e2);
        emps.add(e3);
        //添加该元素时,调用compareTo()方法  e4.10028 - e3.10028 结果为0,不添加
        emps.add(e4);

        for (Employee emp : emps) {
            System.out.println(emp);
        }

    }
}

Map接口

Map称为映射,数据以键值对的形式保存。保存的是键与值的对应关系。

键称为Key,值称为Value,键不能重复,键允许出现一个null作为键,值无限制。

键和值都是引用类型。

如,yyds就是一个键key,代表了一个含义:“永远单身”即为值value。

常用方法作用
size()得到键值对的数量
clear()清空所有键值对
put(Object key,Object value)向集合中添加一组键值对
get(Object key)在集合中根据键得到对应的值
remove(Object key)/remove(Object key,Object key)根据键或键值对移除
keyset()获取键的集合
values()获取值的集合
containsKey(Object key)判断是否存在某个键
containsValue(Object value)判断是否存在某个值
entrySet()得到键值对的集合

HashMap实现类(掌握)

img

  • JDK1.8之后,HashMap采用"数组+链表+红黑树"实现
    • 当没有哈希冲突时,元素保存到数组中
    • 如果出现哈希冲突,在对应的位置上创建链表,元素保存到链表中
    • 如果链表的长度大于8,将链表转换为红黑树
  • 数据采用键值对key-value的形式保存,键不能重复,能用null作为键;值没有限制,键和值都是引用类型
  • 向HashMap集合中添加元素时,原理同HashSet

构造方法

常用构造方法说明
HashMap()创建一个空的映射集合,默认大小为16,加载因子为0.75

常用方法

常用方法参考Map中的方法

遍历集合中元素的方式

遍历List集合

ArrayList<String> nameList = new ArrayList();
nameList.add("Tom");
nameList.add("Jerry");
nameList.add("LiHua");
nameList.add("Danny");

方式一:普通for循环

System.out.println("使用普通for循环遍历");
//方式一:普通for循环
for (int i = 0; i < nameList.size(); i++) {//从0遍历到size()
    String name = nameList.get(i);//通过get(int index)获取指定索引的元素
    System.out.println(name);
}

方式二:增强for循环

System.out.println("使用增强for循环遍历");
//方式二:增强for循环
for (String name : nameList) {
    System.out.println(name);
}

方式三:迭代器

System.out.println("使用迭代器遍历");
//方式三:迭代器
//Collection类型的集合对象.iterator(),获取迭代器
Iterator<String> iterator = nameList.iterator();
// iterator.hasNext()判断集合中是否还有下一个元素
// iterator.next();获取下一个元素
while (iterator.hasNext()) {
    String name = iterator.next();
    System.out.println(name);
}

遍历Set集合

Set hs = new HashSet();
hs.add(123);
hs.add("hello");
hs.add(null);
hs.add(987);

方式一:增强for循环

for(Object o : hs){
    System.out.println(o);
}

方式二:迭代器

Iterator<Object> it = hs.iterator();

while(it.hasNext()){
    System.out.println(it.next());
}

遍历Map集合

Map<Integer, User> hm = new HashMap<>();

User u1 = new User("admin", "123123");
User u2 = new User("tom", "123123");
User u3 = new User("jerry", "123123");
hm.put(1001, u1);
hm.put(1002, u2);
hm.put(1003, u3);

//遍历hashMap
for (Integer id : hm.keySet()) {//遍历键
    //根据键得到对应的值
    System.out.println(id + "\t" + hm.get(id).getUsername());
}

//得到当前hashmap对象中的所有键值对的集合
Set<Map.Entry<Integer, User>> entries = hm.entrySet();
//遍历键值对
for (Map.Entry<Integer, User> entry : entries) {
    System.out.println(entry);
}

泛型

一种规范,常用于限制集合中元素的类型,省去遍历元素时判断是否为对应类型和转型的过程

//集合在定义后,默认可以添加任意类型的数据,但通常情况下,都是保存同一种类型
List  list = new ArrayList();
list.add(123);
list.add(null);
list.add("hello");

//这时如果没有限制类型,使用增强for循环遍历集合中的元素时,就只能使用Object类型变量接收
for(Object o : list){
    
}

用法

在定义集合遍历时,在类后面写上**<引用数据类型>**

集合类或接口<引用数据类型> 集合变量名 = new 集合实现类();

List<String> list = new ArrayList();
//当前集合只能保存String类型的元素
list.add("sdfsdf");
//list.add(123);//无法添加

List<Integer> list2 = new ArrayList();
list2.add(123);

Collections集合工具类

  • Collection是集合的根接口,定义了集合操作元素的方法
  • Collections是集合的工具,定义了集合操作元素的静态方法

常用方法

常用方法说明
Collections.shuffle(List list)打乱List集合中元素的顺序
Collections.sort(List list)对List集合中的元素进行排序,元素必须实现Comparable接口
Collections.swap(List list,int a,int b)交换List集合中元素的索引
Collections.replaceAll(List list,Object oldObj,Object newObj)替换List集合中的旧元素为新元素
Collections.reverse(List list)将List集合中的元素反转
Collections.fill(List list , Object obj)使用指定元素填充List集合
Collections.rotate(List list , int n)将集合中最后n个元素放在最前
Collections.max(Collection col)/min(Collection col)得到集合中的最大/最小值,集合中的元素必须实现Comparable接口

集合和数组之间的转换

  • 集合转换为数组:使用Collection接口中的toArray()方法

    Object[] obj = 集合对象.toArray();
    
    List<Integer> list = new ArrayList();
    list.add(123);
    list.add(63);
    list.add(3);
    
    Integer[] nums =(Integer[]) list.toArray();
    
  • 数组转换为集合

    //一个数组对象
    int[] nums ={11,2,66,3,6,21};
    //定义集合对象
    List list = new ArrayList();
    //遍历数组的同时添加到集合中
    for(int i:nums){
        list.add(i);
    }
    
  • 一组数据转换为集合:使用Arrays工具类中的asList(一组数据)方法

    //通常将数组中的数据直接作为参数
    List<String> strings = Arrays.asList("XX", "aa", "qq", "xx");
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值