Java之集合详解(二)

目录

一.List接口:

二.Set接口:

三.集合部分问题解决:

  3.1. 什么是hashcode

  3.2  为什么Hashse存放自定义类型元素时,要重写对象中的hashCode和equals方法,才能保证元素唯一。

  3.3 List集合扩容机制:


一.List接口:

  1.1 List集合概述:

 

List接口继承自collection接口,我们称实现List接口的对象称之为List集合,在List集合中可以出现重复元素,所有的元素是以线性的存储结构存储。List集合具有一下特点:

  1.元素存取有序,即取出元素和存入元素的顺序一致。(线性的存储结构注定他的存取方式)

  2.List集合带有索引,跟数组索引一样,可以通过索引找到并且操作List集合中的每一个元素。索引0是第一个元素,索引1是第二个元素,以     此类推.....。

  3.List集合中的元素允许重复,可以使用equal方法比较两个元素是否相等。

List接口有两个实现子类arrayList和linkedlist,他们的方法都是来自List中已经定义好的,子类本身不需要额外定义(其他额外需求除外)

    1.2 ArrayList集合:

              1.2.1  ArrayList概述:

1.arrayList集合是list接口的实现子类,继承了list中的方法,并且子类没有其他的太多的改动。

2.arrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以arrayList是最常用的集合。

3.ArrayList底层是由数组实现的,核心的是数组扩容机制,他的长度是可以改变的,并且存储的是引用数据类型的数据。但是也导致一些缺点的出现它可以实现对集合元素的随机访问,但是集合中的元素的移除和插入效率要比另一个实现类低很多,因为他是要通过遍历集合找到元素之后,才插入对应的位置。

           1.2.2  ArrayList使用以及常用方法:

Arrarylist位于Java.util包下,系统不会自动导包,所以我们需要手动导入:

java.util.ArrayList;

Arraylist类实例化成对象,调用方法:

        ArrayList<> arrayList = new ArrayList<>();//实例化对象

             Arraylist常用方法: 

Arrarylist是list接口的子类,list继承了collection接口所有的方法,同时根据List本身的索引特点增加几个方法:

public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。

public E get(int index):返回集合中指定位置的元素。

public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。

public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

代码演示:

import java.util.ArrayList;
import java.util.LinkedList;

public class Shangkelist {
    public static void main(String[] args) {
        /*

         */
        ArrayList arrayList = new ArrayList();//实例化对象
        int sizestart=arrayList.size();
        System.out.println("开始的arraylist长度:"+sizestart);
        arrayList.add(20);//增加元素
        arrayList.add("20");
        arrayList.add("ob");
        arrayList.add(("20"));
        arrayList.add(1,"200");
        int listend=arrayList.size();
        System.out.println("结束的长度"+listend);
        
        boolean res=arrayList.remove("20");
        System.out.println(res);
        System.out.println("=====");
        
        Object str1=arrayList.get(2);//获取元素
//        arrayList.remove(0);
        System.out.println(str1);
        
        for(Object str:arrayList){

//            System.out.println(arrayList);
            System.out.println(str);
        }

注意点:

1.得到集合长度,能更好的根据索引处理集合元素,而得到集合长度的方法不是之前的length()方法,而是size()方法。

2. remove方法不仅可以通过索引删除元素,也可以通过指定对象名删除元素(出现多个相同的元素时会先删除第一个重复的元素)

3.add方法可以指定要加的元素位置和元素,加入到对应的位置(不只是通过加对象名,加在集合最后面)。

1.3 linkedList集合:

    1.3.1 linkedlist集合概述:

1.linkedList是Java.util包下,是list接口的实现子类

2.linkedList集合数据存储的结构是双向链表链表结构。方便元素添加、删除的集合。

3.linkedlist集合和arraylist集合不同的是:arraylist是线性的存储结构,linkedlist是一个双向链表存储结构,这造成两者的优缺点不同,linkedlist对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢,合适用于元素插入和删除

  1.3.2  linkedlist集合的使用以及常用方法:

linkedlist集合和arraylist使用差不多,都是先导入包,实例化对象,调用方法。

        Linkedlist常用方法:

public void addFirst(E e):将指定元素插入此列表的开头。

public void addLast(E e):将指定元素添加到此列表的结尾。

public E getFirst():返回此列表的第一个元素。

public E getLast():返回此列表的最后一个元素。

public E removeFirst():移除并返回此列表的第一个元素。

public E removeLast():移除并返回此列表的最后一个元素。

public E pop():从此列表所表示的堆栈处弹出一个元素。

public void push(E e):将元素推入此列表所表示的堆栈。

public boolean isEmpty():如果列表不包含元素,则返回true

注意点:

1.pop方法弹出的元素是堆栈中第一个元素,实际上就是调用removeFirst方法

2.push()方法压入的位置是栈堆第一个位置 

3.这些都是linkedlist的特有方法,其他不如add,get等方法都是通用的。

4.linkedlist允许元素重复的。

代码实例:

/*
        验证LinkedList的方法

        1.允许元素重复
         */
        LinkedList link = new LinkedList();
        //添加元素
        int sizestar=link.size();
        System.out.println("开始长度长度为"+sizestar);
        link.add("20");
        link.add("20");
        link.add("30");
        link.add("40");

        //添加元素到指定位置
        link.addFirst(1);//第一个位置
        link.addLast(20);

/*
pop和push都是操作第一个元素
 */

        //将这个元素推入栈中
        link.push("推入元素1");

        //指定索引,移除元素
        link.remove(1);

        //弹出元素:弹出的元素是个对象
        Object res1=link.pop();
        System.out.println("弹出元素1:"+res);
        int a=link.hashCode();

        //结束长度
        int size2=link.size();
        System.out.println("长度为"+size2);

        System.out.println("哈希值:");
        //循环输出link元素
        for(int i=0;i<link.size();i++){
            System.out.println(link.get(i));
        }
    }

二.Set接口:

     2.1 Set接口概述:

1.set接口和list接口都是collection接口的子接口,set接口的方法比collection接口的方法没有什么补充,方法基本一致。但是和list方法有很大的一个不同,就是set集合不允许元素重复并且元素是存取顺序不一定一致,而list方法元素可以重复。

2..set接口有三个实现子类TreeSetLinkedHashSet,HashSet。

注意点:

set接口是不允许元素重复的,但是重复不会报错,只会输出第一个重复的值。

  2.2 Hashset集合概述:

1.Hashset是set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的,他的底层支持是Java.util.Map

2.Hashset是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:Hashcode方法和equals方法。

为什么他的元素存取都是无序的?看代码:

 public static void main(String[] args) {
        //不能重复,就算有重复的对像,只会存储一个。
        //无序的
        HashSet<String> set = new HashSet<>();
        set.add(new String("20"));
        set.add("abc");
        set.add("abd");
        set.add("aba");
        set.add("abc");
        //遍历输出
        for (String str:set) {
            System.out.println(str);
        }
    }

运行结果:

可以看到他的输出结果和我们的存入顺序不一致。

为什么他的元素不允许重复呢?看代码:

import java.util.HashSet;

public class OopshangkeSet {
    public static void main(String[] args) {
        //不能重复,就算有重复的对像,只会存储一个。
        //无序的
        HashSet<String> set = new HashSet<>();
        set.add(new String("20"));
        set.add("abc");
        set.add("abc");
        set.add("abc");
        set.add("abc");
        set.add("abd");
        set.add("aba");
        set.add("abc");
        //遍历输出
        for (String str:set) {
            System.out.println(str);
        }
    }
}

运行结果:

 可以看到他的只会输出一个重复的元素。其他的都不会输出,说明其他的重复的元素都没有被存储。

这个不允许重复和无序输出具体原因写在文章最后

2.3 Hashset存储自定义元素:

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一.

代码演示:

重写hashcode和equals方法
*/
import java.util.Objects;
public class Oopset {
    private int age;
    private  String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Oopset)) return false;
        Oopset oopset = (Oopset) o;
        return getAge() == oopset.getAge() &&
                getName().equals(oopset.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getAge(), getName());
    }

    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 String toString() {
        return "Oopset{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

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

测试类:

import java.util.HashSet;
import java.util.Objects;

public class OopsetTest {
    public static void main(String[] args) {
        HashSet<Oopset> hashSet = new HashSet<Oopset>();
//        Oopset student = new Oopset(20, "小强");
//        Oopset student1 = new Oopset(21, "小张");
//        Oopset student2 = new Oopset(21, "小张");
//        Oopset student3 = new Oopset(24, "小李");
        hashSet.add(new Oopset(20, "小强"));
        hashSet.add(new Oopset(21, "小张"));
        hashSet.add(new Oopset(21, "小张"));
        hashSet.add(new Oopset(24, "小李"));
        for (Oopset stu:hashSet) {
            System.out.println(stu);
        }

    }
}

 2.4:linkedHashset概述:

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序

在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。可以保证存放的元素有顺序。

三.集合部分问题总结以及解决:

  3.1. 什么是hashcode

hashcode是通过hash函数也就是某种算法得到的,而hashcode是在hash表中的对应位置,每个对象都有他的hashcode,而一个对象肯定有物理地址,对象的物理地址跟这个hashcode地址不一样,hashcode代表对象的地址说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址,

那么对象如何得到hashcode呢?通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode

  3.2  为什么Hashse存放自定义类型元素时,要重写对象中的hashCode和equals方法,才能保证元素唯一。

 Hashset是如何判断元素相同呢?

  1.先通过hashcode计算出元素的哈希值,如果有已经存在的hashcode与它相同,那么视为相同元素,执行第二步操作。如果不相同,说明元素不相同,添加到集合中。

  2.当哈希值相同时,调用equals方法比较两个元素属性是否相同,相同不添加元素,不相同添加到集合中。

测试: 

public class HashsetTest {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Student("小李"));
        hashSet.add(new Student("小李"));
        int a=hashSet.size();
        System.out.println("元素个数为:"+a);
    }
}
class Student{
    int id;
    String name;
}

 输出结果:

说明两个元素都添加进去了。为什么呢?

  回到hashset是如何判断元素相同的,首先我是分别传入两个相同属性的对象——对象相同,地址值不同。所以hashcode不同,判断机制就会返回false判断他们两个不同,这样集合中就存入了两个同一类的相同对象。所以我们要重写hashcode方法,把判断指向他的属性(名字),这个时候就是获取到属性值name对应的hashcode,而不是地址值的hashcode。
 

@Override
    public int hashCode() {
        return this.hash(name);
    }@Override
    public int hashCode() {
        return this.hash(name);
    }

由于返回的true,进入第二步调用equals方法,我们自定义的类中是没有equals方法的,调用的是Objects类中的equals方法,而它比较的也是地址值,所以我们也要重写equals方法。

 @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return name.equals(student.name);
    }

重写完之后,比较的是对应的属性的内容。最终才判断是否是同一个元素。

 注意点:

Object里的equals比较的是对象的地址,hashcode也是根据地址计算的。重写之后hashcode比较的是属性,equals比较的也是属性

equal()比较的是内容相等,==比较的是全等(内容以及地址相等),hahscode比较的是地址(hash表中的地址)相等。

  3.3  List集合扩容机制:

明天补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值