第十二章 集合框架(二)

1.练习回顾

public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List list = new LinkedList();
        String name;
        String sex;
        while(true){
            System.out.print("请输入姓名:");
            name = scanner.next();
            System.out.print("请输入性别:");
            sex = scanner.next();
            Perple perple = new Perple(name,sex);
            if(list.contains(perple)){
                System.out.println("这位同志已存在,请重新输入!");
                continue;
            }
            list.add(perple);
            System.out.println("是否继续(y/n)?");
            if(scanner.next().equals("n")){
                break;
            }
        }
        //System.out.println("录入的学生信息是:\n"+list);
        for (Object obj: list) {
            System.out.println(obj);
        }
    }
}
public class Perple {
    String name;
    String sex;

    public Perple(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

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

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Perple[" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ']';
    }
}

2.Arraylist使用

2.1遍历集合

for+索引遍历

for (int i = 0; i < arrayList.size(); i++) {
            //默认存储的数据都是Object类型的
            Object obj = arrayList.get(i);
            System.out.println(obj);
        }

for Each遍历

 for (Object obj:arrayList) {
            System.out.println(obj);
        }

注:

        不要在遍历ArrayList 过程中使用remove()方法。

①for+索引遍历来的方式:

//错误使用①        
for (int i = 0; i < arrayList.size(); i++) {
            arrayList.remove(i);
        }

使用remove方式移除元素之后arrayList会进行重排,导致元素的索引改变,从而漏掉一些元素。

②for Each 遍历:

//错误使用②
for (Object obj: arrayList) {
            arrayList.remove(obj);
        }

该方式则是直接会产生异常:ConcurrentModificationException(并发修改异常)。

③解决方式:

倒序遍历

        for (int i = arrayList.size()-1; i>=0; i--) {
            arrayList.remove(i);
        }

使用迭代器(Iterator)

        Iterator it = arrayList.iterator();
        while (it.hasNext()){
            if(it.next()=="aaa"){
                it.remove();
            }
        }

2.2 ArrayList底层源码

        对于ArrayList而言,它实现List接口、底层使用数组保存所有元素,其操作上基本是对数组的操作。

2.2.1 ArrayList无参构造:ArrayList()

    public ArrayList() {
        //elementData 是 ArrayList 底层实际存储数据的对象数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    transient Object[] elementData; // non-private to simplify nested class access

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //所以new ArrayList的本质就是创建了一个空的Object类型的数组

2.2.2 ArrayList有参构造:ArrayList(int initialCapacity)

//initialCapacity:初始容量
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //初始容量大于0,则按初始容量创建数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //等于0,则直接赋给一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //小于0,则抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

2.2.3 添加元素:add(E e)扩容原理

    public boolean add(E e) {
        //扩容数组
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        将数据存储到数组中,并且元素个数+1
        elementData[size++] = e;
        return true;
    }
    //扩容的具体实现
    private void ensureCapacityInternal(int minCapacity) {
        //判断当前ArrayList是否是空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //private static final int DEFAULT_CAPACITY = 10
            //取二者中的较大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        //记录该list在结构上被修改的次数
        modCount++;

        // overflow-conscious code
        //当前数组长度小于扩容长度时,执行grow()
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        
        // overflow-conscious code
        //原始容量
        int oldCapacity = elementData.length;
        //新的容量 >>移位运算符 >>1 二进制数右移一位  相当于原数 /2 
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //Array.copyOf:扩容的具体实现
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

2.2.4 删除元素:remove(Object o)

    public boolean remove(Object o) {
        //判断数据是否为空
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            //通过系统进行错位复制的方式,删除元素
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

2.2.5 清空集合:clear()

//将每一个元素设置为空,并长度设置为0
public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

2.2.6 查找集合中某个元素的位置:indexOf(Object o)

//所以indexOf找不到的时候返回值是 -1
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]))
                    return i;
        }
        return -1;
    }

2.2.7 返回集合元素长度:size()

//size 记录着元素的个数  
public int size() {
        return size;
    }

3.Set接口及其实现类

3.1 Set接口特点

①Set接口是无序的;

②Set接口中的数据不允许重复;

③Set接口无法通过下标访问数据;

④查找慢,插入删除快(底层数据结构是哈希表、链表和红黑树);

⑤Set集合使用equals()和hashCode()方法实现元素去重。

3.2 HashSet实现类

HashSet特点: 

①HashSet是Set接口的实现类;

②线程不安全。

    public static void main(String[] args) {
        //Set:1.无序 2.不重复 3.没有下标
        Set set = new HashSet();
        set.add("bbb");
        set.add("bbb");
        set.add(true);
        set.add(12.234);
        set.add("aaa");
        set.add("aaa");
        set.add(100);
        set.add(200);
        set.add(new Student("张三",20));
        //打印集合内容
        //System.out.println(set);
        //打印长度
        //System.out.println(set.size());
        //set无法按照索引获取数据,因为set是无序的没有下标
        //删除
        //set.remove("aaa");
        //System.out.println(set);
        //包含某个数据
        //System.out.println(set.contains("bbb"));
        //是否为空
        //System.out.println(set.isEmpty());
        //System.out.println("================================");
        //遍历set集合
        //方式1:for Each
        for (Object obj : set){
            System.out.println(obj);
        }
        System.out.println("============================");
        //方式2: Iterator迭代器
        //通过集合对象的iterator()方法,返回一个Iterator对象,通过这个对象可以遍历集合元素
        Iterator it =  set.iterator();
        //it.hasNext():判断集合是否有数据,返回true/false
        //it.next():读取集合中的一个数据
        while (it.hasNext()){
            Object o = it.next();
            System.out.println(o);
        }
    }

HashSet避免对象重复的规则:

①如果对象的hashCde值不同,则不需要用equals方法判断,直接存到HashSet中;

②如果对象的hashCode值相同,需要用equals方法进行比较,如果结果为true,则视为相同元素,不存储;如果结果为false,则视为不同元素进行存储。

 

         如果对象元素要存储到HashSet中,必须覆盖hashCode方法和equals方法,才能从对象内容的角度确保唯一性。

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

    public Student(){
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //重写equals
    @Override
    public boolean equals(Object o) {
        if(this==o){
            return true;
        }
        if(!(o instanceof Student)){
            return false;
        }

        Student student = (Student) o;
        if(Objects.equals(this.name,student.getName()) &&
                Objects.equals(this.age,student.age)){
            return true;
        }
        return false;
    }

    //重写hashCode,返回hash值,通过hash值得到要存储到数组的下标
    //hash值一样的数据存储到同一个数组空间,后再用equals进行内容比较

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public static void main(String[] args) {
    Student s1 = new Student("张三","男",20);
    Student s2 = new Student("张三","男",20);
    Student s3 = new Student("李四","男",20);
    // Object中的HashCode默认返回的是对象的内存地址
    //System.out.println(s1.hashCode());
    //System.out.println(s2.hashCode());
    Set s = new HashSet();
    s.add(s1);
    s.add(s2);
    s.add(s3);
    System.out.println(s);
    // hash运算(默认调用数据的hashCode()方法实现)结果是相同的前提下,才会进行相
    // hash运算结果不同,是无法去重的
    /*String s = "sdf";
    String ss = "sdf";
    System.out.println(s.hashCode());
    System.out.println(ss.hashCode());
    */
}

3.3 迭代器Iterator

        

        HashSet类中没有提供根据集合索引获取索引对应的值的⽅法, 因此遍历HashSet时需要使⽤Iterator迭代器。

Iterator的主要方法
返回类型方法描述
booleanhasNext()如果有元素可迭代
Objectnext()返回迭代的下一个元素
public static void main(String[] args) {
    // LinkedHashSet:链表结构的Set集合
    // 1.不重复 2.能保证添加的次序(添加和输出次序一致)
    Set set = new LinkedHashSet();
    set.add("c");
    set.add("a");
    set.add("d");
    set.add("b");
    //System.out.println(set);
    // 迭代器遍历集合
    Iterator it = set.iterator();
    while (it.hasNext()){
        System.out.println(it.next());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值