Java集合之Collection接口

1 - Java集合介绍

/*
1. 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储。

2. 另一方面,使用Array存储对象方面具有一些弊 端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。

  ①数组在内存存储方面的特点:

    数组初始化以后,长度就确定了。

    数组声明的类型,就决定了进行元素初始化时的类型

  ②数组在存储数据方面的弊端:

    数组初始化以后,长度就不可变了,不便于扩展

    数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数

    数组存储的数据是有序的、可以重复的。---->存储数据的特点单一

3. Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
*/

package com.lzh.java1;
/*
集合框架概述:
1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到硬盘层面的存储。
2. 数组在存储多个数据方面的特点:
    > 一旦初始化以后,其长度就确定了
    > 数组一旦定义好,其元素的类型也确定了,也就只能操作指定类型的数据了,比如:String[] array;int[] arr

3. 数组在存储多个数据方面的缺点:
    > 一旦初始化以后,其长度就不可修改
    > 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便
    > 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
    > 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足

 */
public class CollectionTest {
}
集合框架概述

2 - Java集合的使用场景

 

3 - Java集合Collection 和 Map 两种体系

/*
Java 集合可分为 CollectionMap 两种体系:

  1 Collection接口:单列数据,定义了存取一组对象的方法的集合

    ①List:元素有序、可重复的集合 --> "动态"数组

    ②Set:元素无序、不可重复的集合 --> 数学层面的集合(无序,确定,互异)

  2 Map接口:双列数据,保存具有映射关系“key-value对”的集合 --> 数学函数 y = f(x)
*/

Collection接口单列集合,用来存储一个一个的对象。其中List接口:用来存储,有序可重复的数据,Set接口:用来存储,无序,不可重复的数据

Map接口:双列集合,用来存储一对(key - value)的一对数据

4 - Collection接口方法

/*
Collection接口

  1 Collection 接口是 List、SetQueue 接口的父接口,该接口里定义的方法 既可用于操作 Set 集合,也可用于操作 ListQueue 集合。

  2 JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:SetList)实现。

  3 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都 当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型

  4 结论:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()

*/

/*
1、添加
    ① add(Object obj)
    ② addAll(Collection coll)
    
2、获取有效元素的个数
    ① int size()
    
3、清空集合
    ① void clear()
    
4、是否是空集合
    ① boolean isEmpty()
    
5、是否包含某个元素
    ① boolean contains(Object obj):是通过元素的equals方法来判断是否 是同一个对象
    ② boolean containsAll(Collection c):也是调用元素的equals方法来比 较的。拿两个集合的元素挨个比较。
    
6、删除
    ① boolean remove(Object obj) :通过元素的equals方法判断是否是 要删除的那个元素。只会删除找到的第一个元素
    ② boolean removeAll(Collection coll):取当前集合的差集
    
7、取两个集合的交集
    ① boolean retainAll(Collection c):把交集的结果存在当前集合中,不 影响c
    
8、集合是否相等
    ① boolean equals(Object obj)
    
9、转成对象数组
    ① Object[] toArray()
    
10、获取集合对象的哈希值
    ① hashCode()
    
11、遍历 
    ① iterator():返回迭代器对象,用于集合遍历
 */
Collection接口中的方法
package com.lzh.java1;

import org.junit.Test;
import java.util.*;

/*
Collection接口中声明的方法测试
结论:
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
 */
public class CollectionTest {
    // Collection接口中方法的使用
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        // add(Object e)  将元素e添加到集合中
        coll.add(123); // 自动装箱
        coll.add("a");
        coll.add("b");
        coll.add(new Date());

        // size() 获取集合中元素的个数
        System.out.println(coll.size());

        // addAll(Collection coll1) 将集合coll1中的所有元素添加到当前的集合中
        Collection coll1 = new ArrayList();
        coll1.add(222);
        coll1.addAll(coll);
        System.out.println(coll1.size()); // 5
        System.out.println(coll1);

        // isEmpty() 判断当前集合是否为空
        System.out.println(coll.isEmpty()); // false

        // clear() 清空集合元素
        coll.clear();
        System.out.println(coll.isEmpty()); // true
    }

    @Test
    public void test2(){
        Collection collection1 = new ArrayList();
        collection1.add(13);
        collection1.add(22);
        collection1.add(new String("alex"));
        collection1.add(false);
        collection1.add(new Person("alex",22));
        // System.out.println(collection1);

        // contains(Object obj):是通过元素的equals方法来判断是否 是同一个对象
        boolean isContains = collection1.contains(13);
        System.out.println(isContains);
        System.out.println(collection1.size());
        System.out.println(collection1.contains(new Person("alex",22))); // true

        // containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中。
        Collection collection2 = Arrays.asList(13,false);
        System.out.println(collection1.containsAll(collection2)); // true
    }

    @Test
    public void test3(){
        // 集合 --> 数组 toArray() 返回值为 Object[]
        Collection collection = new ArrayList();
        for(int i = 0;i < 5;i++){
            collection.add(i);
        }
        Object[] arrays = collection.toArray();
        System.out.println(arrays.getClass()); // class [Ljava.lang.Object;
        // 遍历数组
        for(int i = 0;i < arrays.length;i++){
            System.out.println(arrays[i]);
        }

        // 数组 --> 集合 调用Arrays类的静态方法asList()
        Collection collection1 = Arrays.asList(11,"adf",22);
        System.out.println(collection1.getClass()); // class java.util.Arrays$ArrayList
    }

    @Test
    public void test4(){
        // iterator()  返回Iterator接口的实例,用于遍历集合元素。
        Collection collection = new ArrayList();
        for(int i = 0;i < 5;i++){
            collection.add("list"+1);
        }
        Iterator iterator = collection.iterator();
        // 集合元素的遍历操作,使用迭代器Iterator接口
    }
}
class Person{
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        System.out.println("执行 equals()...");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
Collection接口中声明的方法测试

5 - Iterator迭代器接口

/*

Iterator迭代器介绍:

  1 Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。

  2 GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元 素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公 交车上的售票员”、“火车上的乘务员”、“空姐”。

  3 Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所 有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了 Iterator接口的对象。

  4 Iterator 仅用于遍历集合Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。

  5 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前
*/

 Iterator接口的方法

package com.lzh.java1;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
集合元素的遍历操作,使用迭代器Iterator接口
1. 内部的方法:hasNext() 和 next()
2. 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
3. 内部定义了remove(),可以在遍历的时候,删除集合中的元素,此方法不同于集合直接调用remove()
 */
public class IteratorTest {
    @Test
    public void test1(){
        // iterator()  返回Iterator接口的实例,用于遍历集合元素。
        Collection collection = new ArrayList();
        for(int i = 0;i < 5;i++){
            collection.add("list"+i);
        }
        Iterator iterator = collection.iterator();
        // 遍历集合:方式1 不推荐使用
        for(int i = 0;i < collection.size();i++){
            System.out.println(iterator.next()); // 超过5次就报错(局限性)
        }
        // 遍历集合:方式2 推荐使用
        // hasNext()与next()搭配使用
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test2(){
        // iterator中的remove()
        Collection collection = new ArrayList();
        for(int i = 0;i < 5;i++){
            collection.add("list"+i);
        }
        Iterator iterator = collection.iterator();
        while(iterator.hasNext()){
            if("list2".equals(iterator.next())){
                iterator.remove();
            }
        }

        Iterator iterator1 = collection.iterator(); // 重新获取迭代器
        while(iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
        /*注意: 
            1 Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方 法,不是集合对象的remove方法。 
            2 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException。
         */
    }
}
迭代器Iterator接口的使用

迭代器的执行原理

6 - 使用 foreach 循环遍历集合元素

/*

foreach循环:

  1 Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。

  2 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。

  3 遍历集合的底层调用Iterator完成操作

  4 foreach还可以用来遍历数组。
*/

package com.lzh.java1;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/*
1 Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
2 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
3 遍历集合的底层调用Iterator完成操作。
4 foreach还可以用来遍历数组。
 */
public class foreachTest {
    @Test
    public void test1(){
        Collection collection = new ArrayList();
        for(int i = 0;i < 5;i++){
            collection.add("list"+i);
        }
        // for(集合中元素的类型 局部变量 : 集合对象/数组){}
        // 内部任然调用迭代器
        for(Object i:collection){
            System.out.println(i);
        }
    }

    // 遍历数组
    @Test
    public void test2(){
        int[] intArray = new int[]{1,2,3,4,5,6,7,8,9,10};
        for(int i:intArray){
            System.out.println(i);
        }
    }

    // 面试题:输出结果?
    @Test
    public void test3(){
        String[] arr = new String[3];

        for(String i:arr){
            i = "mm";
            System.out.println(i);
        }

        for(int i = 0;i < arr.length;i++){
            System.out.println(arr[i]); // null
        }
    }
}
foreach循环的使用

7 - Collection子接口之一:List接口

/*
List接口:

  1 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组

  2 List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。

  3 List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据 序号存取容器中的元素。

  4 JDK APIList接口的实现类常用的有:ArrayList、LinkedList和Vector。
*/

/*
List接口:"动态"数组
List接口的3个实现类:ArrayList、LinkedList和Vector

面试题:ArrayList \ LinkedList \ Vector 3者之间的异同?
    同:3个类都实现了List接口,存储数据的特点相同(有序,可重复)
    异:
        ArrayList(List接口主要实现类):线程不安全,执行效率高。底层用Object[] elementData数组进行存储
        LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向列表存储
        Vector(List接口古老实现类):线程安全,执行效率偏低。底层用Object[] elementData数组进行存储

ArrayList源码分析:
    JDK7
    ArrayList list = new ArrayList(); // 底层创建了长度为10的Object[] 数组elementData
    list.add(123); // elementData[0] = new Integer(123);
    ...
    list.add(11); // 如果此次的添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
    结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity);

    JDK8 中ArrayList的变化
    ArrayList list = new ArrayList(); // 底层Object[] elementData初始化为{},并没有创建长度为10的数组
    list.add(123); // 第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData[0]
    ...
    后续的扩容问题与JDK7一样
    结论:JDK7中的ArrayList的对象的创建类似于单例模式的饿汉式,而JDK8中ArrayList的对象的创建类似于单例模式的懒汉式,延迟了数组的创建,节省内存。

LinkedList的源码分析:
    LinkedList list = new LinkedList(); // 内部声明了Node类型的first和last属性,默认值为null
    list.add(123); // 将123封装到Node中,创建Node对象

    其中。Node定义为:体现了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;
        }
    }
 */
面试题:ArrayList \ LinkedList \ Vector 3者之间的异同?

 

List实现类之一:ArrayList

  1 ArrayListList 接口的典型实现类、主要实现类

  2 本质上,ArrayList是对象引用的一个”变长”数组

  3 ArrayList的JDK1.8之前与之后的实现区别?

    ① JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组

    ② JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组

  4 Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是 Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合

 

 

 List实现类之二:LinkedList 

  1 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

  2 新增方法:

    1 void addFirst(Object obj)

    2 void addLast(Object obj)

    3 Object getFirst()

    4 Object getLast()

    5 Object removeFirst()

    6 Object removeLast()

 

  3 LinkedList双向链表,内部没有声明数组,而是定义了Node类型的firstlast, 用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基 本结构。Node除了保存数据,还定义了两个变量:

    1 prev变量记录前一个元素的位置

    1 next变量记录下一个元素的位置

 

List 实现类之三:Vector

  1 Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。

  2 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时, 使用LinkedListVector总是比ArrayList慢,所以尽量避免使用。

  3 新增方法:

    1 void addElement(Object obj)

    2 void insertElementAt(Object obj,int index)

    3 void setElementAt(Object obj,int index)

    4 void removeElement(Object obj)

    5 void removeAllElements()

 

/*
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来 操作集合元素的方法。

    1 void add(int index, Object ele)  在index位置插入ele元素

    2 boolean addAll(int index, Collection eles)  从index位置开始将eles中 的所有元素添加进来

    3 Object get(int index)  获取指定index位置的元素

    4 int indexOf(Object obj)  返回obj在集合中首次出现的位置

    5 int lastIndexOf(Object obj)  返回obj在当前集合中末次出现的位置

    6 Object remove(int index)  移除指定index位置的元素,并返回此元素

    7 Object set(int index, Object ele)  设置指定index位置的元素为ele

    8 List subList(int fromIndex, int toIndex)  返回从fromIndex到toIndex 位置的子集合
 */
List接口方法
package com.lzh.java1;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ListMethodTest {
    @Test
    public void test1(){
        ArrayList list1 = new ArrayList();
        for(int i = 0;i < 5;i++){
            list1.add("list"+i);
        }

        // 1 void add(int index, Object ele)  在index位置插入ele元素
        list1.add(0,"第一个元素");
        System.out.println(list1); // [第一个元素, list0, list1, list2, list3, list4]

        // 2 boolean addAll(int index, Collection eles)  从index位置开始将eles中 的所有元素添加进来
        List list2 = Arrays.asList(1,2,3);
        list1.addAll(list2);
        System.out.println(list1.size()); // 9

        // 3 Object get(int index)  获取指定index位置的元素
        System.out.println(list1.get(0)); // 第一个元素
        System.out.println(list2.get(0)); // 0

        // 4 int indexOf(Object obj)  返回obj在集合中首次出现的位置,如果不存在返回 -1

        int index = list1.indexOf(3);
        System.out.println(index); //  8
    }

    @Test
    public void test2(){
        ArrayList list = new ArrayList();
        List l = Arrays.asList("a",1,5,3);
        list.addAll(l);
        list.add(3);
        // System.out.println(list);

        // 5 int lastIndexOf(Object obj)  返回obj在当前集合中末次出现的位置
        int index = list.lastIndexOf(3);
        System.out.println(index); // 4

        // 6 Object remove(int index)  移除指定index位置的元素,并返回此元素
        Object remove = list.remove(0);
        System.out.println(remove); // a
        System.out.println(list); // [1, 5, 3, 3]

        // 7 Object set(int index, Object ele)  设置指定index位置的元素为ele
        Object p = list.set(1, "hello");
        System.out.println(p); // 5
        System.out.println(list); // [1, hello, 3, 3]

        // 8 List subList(int fromIndex, int toIndex)  返回从fromIndex到toIndex 位置的子集合
        List newList = list.subList(2,4);
        System.out.println(newList); // [3,3]
    }
}
List接口方法测试

 

8 - Collection子接口之二:Set接口

/*

set接口:

  1 Set接口是Collection的子接口,set接口没有提供额外的方法(用的都是Collection声明的方法)

  2 Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。

  3 Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

  4 存储数据特点:无序、不可重复
*/

package com.lzh.java2;

import org.junit.Test;

import java.util.*;

/*
1 Set接口框架:
    /----Set接口:存储无序、不可重复的数据  -->高中讲的"集合"
        /----HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值
        /----LinkedHashSet:Linked(链表),作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
        /----TreeSet:可以按照添加对象的指定属性,进行排序,要求:添加的数据是相同类的对象
            ①两种排序方式:自然排序(comparable) 和 定制排序(comparator)
            ②自然排序中,比较两个对象是否相同的标准为:compareTo() 返回0,不再是equals()
            ③定制排序中,比较两个对象是否相同的标准为:compare() 返回0,不再是equals()

    要求:
        1 向Set中添加的数据,其所在的类一定要重写 hashCode() 和 equals()
        2 重写的 hashCode() 和 equals() 尽可能保持一致性:相等的对象必须具有相等的散列表

2 理解Set接口存储数据:无序、不可重复性。
    ① 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值进行存储
    ② 不可重复:保证添加的元素按照equals()判断时,不能返回true。即相同的元素只能添加一个

3 添加元素的过程:以HashSet为例
    我们向HashSet中添加元素a,首先调用元素a所在类的HashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中存放的位置(索引),
    判断数组此位置上是否已经有元素:
        如果此位置上没有其他元素,则元素a添加成功。 --> 情况1
        如果此位置上没有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
            如果hash值不相同,则元素a添加成功。 --> 情况2
            如果hash值相同,进而需要调用元素a所在类的equals()方法:
                equals()返回true,元素a添加失败
                equals()返回false,则元素a添加成功 --> 情况3

    对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据已链表的方式存储
    JDK 7:元素a放到数组中,指向原来的元素
    JDK 8:原来的元素在数组中,指向元素a

    总结:HashSet底层:数组+链表的结构

 */
public class SetTest {
    @Test
    public void test1(){
        Set set = new HashSet();
        for(int i = 0;i < 5;i++){
            set.add("list"+i); // 有序存储
        }
        set.add(new User("howie",21));
        set.add(new User("howie",21));

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next()); // 无序输出
        }
    }

    // LinkedHashSet遍历内部数据
    // LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
    // 优点:对于频繁遍历操作,LinkedHashSet效率高于HashSet
    @Test
    public void test2(){
        Set set = new LinkedHashSet();
        for(int i = 0;i < 5;i++){
            set.add("list"+i); // 有序存储
        }

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next()); // 有序输出
        }
    }
}

class User{
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
Set接口框架

1-Set实现类之一:HashSet

/*

HashSet介绍:

  1 HashSetSet 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。

  2 HashSetHash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。

 

  3 HashSet 具有以下特点:

    ① 不能保证元素的排列顺序

    ② HashSet 不是线程安全的

    ③ 集合元素可以是 null

 

  4 HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。

  5 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”

  6 向HashSet中添加元素的过程:

    ①当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法 来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象 在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)

    ②如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。

  7 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相 等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功
*/

/*
1 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。 

2 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。

3 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值
*/
重写 hashCode() 方法的基本原则
/*
以自定义的Customer类为例,何时需要重写equals()?

1 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是 要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。

2 因此,违反了“相等的对象必须具有相等的散列码”。

3 结论:复写equals方法的时候一般都需要同时复写hashCode方法。通 常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
*/
重写 equals() 方法的基本原则

2-Set实现类之二:LinkedHashSet

/*

LinkedHashSet介绍:

  1 LinkedHashSetHashSet 的子类

  2 LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。

  3 LinkedHashSet插入性能略低于 HashSet但在迭代访问 Set 里的全 部元素时有很好的性能。

  4 LinkedHashSet 不允许集合元素重复
*/
底层结构

3-Set实现类之三:TreeSet

  1 TreeSet SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

  2 TreeSet底层使用红黑树结构存储数据

  3  新增的方法如下: (了解)

    1 Comparator comparator()

    2 Object first() Object last()

    3 Object lower(Object e)

    4 Object higher(Object e)

    5 SortedSet subSet(fromElement, toElement)

    6 SortedSet headSet(toElement)

    7 SortedSet tailSet(fromElement)

  4 TreeSet 两种排序方法:自然排序定制排序。默认情况下,TreeSet 采用自然排序

 

 

posted @ 2020-06-13 23:06  赖正华  阅读(725)  评论(0编辑  收藏  举报