Java六十: Java集合 - - Collection接口(List接口、Set接口)

集合 - - Collection接口(List接口、Set接口)

导读
  1. 集合,进行多对象的统一存储,更贴近Java的面向对象的语法框架,根据不同应用场景的需求,对集合进行了三种分类:List(动态数组)、Set(高中的”集合“)、Map(高中的”函数“),并提供了增删改查、数组集合互转、遍历等公用方法
  2. List接口:主要是ArrayList、LinkedList两个实现类,并掌握其底层添加数据的原理,以及针对List集合中数据特有的有序性,提供了针对索引值操作操作的系列方法。
  3. Set接口:主要是HashSet、LinkedSet两个实现类,其底层添加数据原理与HashMap有关,需要参HashMap的源码分析,针对Set集合中数据的无序、不可重复性,所有的对象性数据的对应类必须重写equals方法和hashCode方法
引用
  1. 集合(Collection)是方便对多个对象的统一存储,以前学过的储存结构式是数组(Array)

    ① 共同点:都是对多个数据进行存储的结构,简称:Java容器

    :此处的存储是指内存层面的存储,不涉及到永久化存储

    ② 数组的特点

    一旦定义,其长度就确定了
    一旦定义,其元素类型就确定了

    数组的弊端

    初始化后,长度不可改变
    提供的方法有限,不便于增删,效率不高
    获取数组中实际元素个数的需求,数组没有现成的属性或方法使用
    数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,无法满足,故引进集合

    集合的优势

    可以动态的存储数量不等、类型不同的多个对象,还可以保存具有映射关系的关联数组
    封装一个类,类中提供方法,再用这个类的对象去装数据,去调用方法,呈现其很有特色的一些需求(增删改查),这就是对集合框架的一个很正常的需求

  2. 集合框架

    什么容器干什么事,故集合中会有各种不同的容器类型(接口类型)

    Collection接口:单列集合,用来存储一个一个的对象

    List:存储有序、可重复的数据 - - - > " 动态 "数组

    | - - - ArrayList、LinkedList、Vector

    Set: 存储无序、不可重复的数据 - - - > 高中讲的“ 集合 ”

    | - - - HashSet、LinkedHashSet、TreeSet 在这里插入图片描述

    Map接口:双列集合,用来保存一对(具有映射关系“ key - value 对”) 一对的数据 - - - > 高中函数:y = f(x)

    | - - - HashMap、LinkedHashMap

    | - - -TreeMap

    | - - -Hashtable、Properties 在这里插入图片描述

  3. 现实应用(JSON) 在这里插入图片描述
一、Collection接口中的API
  • List接口和Set接口都实现了Collection接口,所以Collection接口中的方法就会通用
  • 向Collection接口实现类的对象中添加数据obj时,要求obj所在类要重写equals( ) 方法
  • jdk8新特性StreamAPI对于集合数据的操作 详见 Java8新特性 - - Stream类
  1. add():为集合添加元素
    @Test
        public void test(){
            Collection coll = new ArrayList();
            // 1.添加字符串
            coll.add("AA");
            coll.add(new String("Tom"));
            // 2.添加基本类型数据
            coll.add(123);        
            coll.add(false);
            // 3.添加自定义类:Person类重写了toString和equals方法
            coll.add(new Person("Jerry",20));	
            // 输出为集合形式[AA, Tom, 123, false, {Jerry,20}]
            System.out.println(coll);
        }
    
  2. size():返回该集合中的元素个数
    	// 5
    	System.out.println(coll.size());
    
  3. addAll(Collection c):将集合c中的元素全部添加到调用集合中
    	// 1.创建集合coll2
    	collection coll2 = new ArrayList();
    	coll2.add("BB");
    	coll2.add(123);
    	// 2.将集合coll2添加到集合coll中
    	coll.addAll(coll2);
    	// 输出结果为:[AA, 123, Tom, false, {Jerry,20},BB, 123]
    	System.out.println(coll); 		       
    
  4. clear():清空集合中的元素,为[ ],但不是null
    	// 清空集合中的元素,输出为:[]
    	coll.clear();
    	System.out.println(coll);
    
  5. contains(Object obj) 判断集合中是否含有obj
    	// 判断集合中是否包含某元素,判断结果为:true
    	boolean contain = coll.contains(123);
    	System.out.println(contain);
    

    对于自定义类,如果没有重写equals()方法,则 contains方法返回 false

    	/* 
    	 1.输出结果为true, 因为Person类中重写了equals方法
    	 2.contain方法会在调用obj对象时,判断其所在类是否重写了equals方法
    	*/
    	boolean contain1 = coll.contains(new Person("Jerry",20));
    	System.out.println(contain1);	
    
  6. containsAll(Collection c):coll2集合中的元素是否都包含在coll集合中
    	// 1.新建集合coll3
    	Collection coll3 = Arrays.asList(123,"AA");
    	// 2.判断集合coll中是否包含集合coll3的所有元素,输出结果为true
    	System.out.println(coll.containsAll(coll2));
    
  7. remove(Object o) 移除集合中的元素
    	// 将元素123从集合coll中移除
        coll.remove(123);
        // 移除元素后的集合coll:[AA, Tom, false, {Jerry,20}]
        System.out.println(coll);
    
  8. removeAll(Collection c) 移除集合中包含有集合 c 中的元素,差集操作
    	// 从集合coll中移除集合coll3中的所有元素
    	coll.removeAll(coll3);
    	// 移除后的集合coll:[Tom, false, {Jerry,20}]
    	System.out.println(coll);
    
  9. retainAll(Collection c) 交集:将交集运算的结果返回
    	// 新建集合coll4
    	Collection coll4 = Arrays.asList("123",new Person("Jerry",20));
    	// 从集合coll中找到与集合coll4共有的元素,并移除其它元素
    	coll.retainAll(coll4);
    	// 交集运算后的coll只包含一个共有元素:[{Jerry,20}]
    	System.out.println(coll);
    
  10. equals(Collection c):比较两个集合是否相等,元素相同并且顺序相同
    	// 新建集合
    	Collection coll5 = Arrays.asList("AA",123,new String("Tom"),false,new 			Person("Jerry",20));
    	// false,此时的coll只剩一个元素,如果是原先状态,则输出true
    	System.out.println(coll.equals(coll5));
    
  11. hashCode() 返回该集合的哈希值
    	// 1031980562
    	System.out.println(coll.hashCode());
    
  12. toArray() 集合 - - > 数组
    @Test
        public void test2(){
            // 1.新建集合coll6
            Collection coll = new ArrayList();
            coll.add("AA");
            coll.add(123);
            coll.add(new String("Tom"));
            coll.add(false);
            coll.add(new Person("Jerry",20));
    
            // 2.将集合coll6转变为数组格式
            Object[] arr = coll6.toArray();
            // 将数组arr用增强for循环遍历输出:AA  123  Tom false  {Jerry,20}
            for (Object o : arr){
                System.out.println(o);
            }
    
  13. Arrays.asList() 数组 - - > 集合
    	// 1.新建int型数组(基本数据类型数组)
    	int[] nums1 = new int[]{123, 345};
    	// 1.1 将数组转换为集合
    	List list1 = Arrays.asList(nums1);
    	// 1.2此中的new int[]{123, 345}相当于是集合中一个元素,输出为地址值[[I@3d82c5f3]
        System.out.println(list1);
    
    	// 2.新建Integer型数组(包装类数据型数组)
    	Integer[] nums2 = new Integer[]{123, 345};
    	// 1.1 将数组转换为集合
    	List list2 = Arrays.asList(nums2);
    	// 1.2 输出结果为[123,456]
        System.out.println(list2);
    
    	// 3.新建String型数组
    	String[] nums3 = new String[]{"123"," 345"};
    	// 1.1 将数组转换为集合
    	List list3 = Arrays.asList(nums3);
    	// 1.2 输出结果为[123,456]
        System.out.println(list3);
    

    UnsupportedOperationException的原因及解决方案_

  14. 基于Iterator接口的集合遍历
    • 迭代器:提供一种方法访问一个容器(container)对象中各个元素,而又不需要暴露该对象的细节。

    • ② 迭代器模式,为容器而生

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

    • ④ Collection接口继承了java.lang.Iterable接口,该接口中有一个**iterator()**方法,用以返回一个实现了Iterator接口的对象

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

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

    • ⑦ 常用方法

      thodsummary
      hasNext()判断集合的iterator对象中的是否还有元素
      next()返回集合中的下一个元素
      remove()可以在遍历集合的过程中,删除某些符合要求的元素
    • ⑧代码1【hasNext()、next()】

      @Test
          public void iteratorTest(){
              // 1.创建集合coll
              Collection coll = new ArrayList();
              coll.add("AA");
              coll.add(123);
              coll.add(new String("Tom"));
              coll.add(false);
              coll.add(new Person("Jerry",20));
              
      		// 2.用iterator方法返回一个Iteretor对象
              Iterator iterator = coll.iterator();
              // 3.调用hasNext方法判断该集合中是否有元素
              while (iterator.hasNext()){
                  // 4.调用next方法返回集合的下一个元素
                  System.out.println(iterator.next());
              }        
          }
      
    • 代码2:【remove()】

       @Test
          public void iteratorTest1(){
              Collection coll = new ArrayList();
              coll.add("AA");
              coll.add(123);
              coll.add(new String("Tom"));
              coll.add(false);
              coll.add(new Person("Jerry",20));
              Iterator iterator1 = coll.iterator();
              while (iterator1.hasNext()){
                  // 1.在遍历元素的过程中,将符合条件的元素删除
                  Object obj = iterator1.next();
                  if ("Tom".equals(obj)){
                      iterator1.remove();
                  }
              }        
              // 2.再次遍历集合,要新建Iterator对象,将其游标重新定位到集合的第一元素之前 
              Iterator iterator2 = coll.iterator();
              // 输出:AA  123  false  {Jerry,20}
              while (iterator2.hasNext()){
                  System.out.print(iterator2.next() + "\t");
              }
          }
      
  15. foreach循环遍历(增强for循环)

    内部调用的还是Interator迭代器(可用Debug查看)

    在这里插入图片描述

    • 代码示例

      @Test
          public void foreachTest(){
              
              // 新建集合coll
              Collection coll = new ArrayList();
              coll.add("AA");
              coll.add(123);
              coll.add(new String("Tom"));
              coll.add(false);
              coll.add(new Person("Jerry",20));
      		// 1.增强for循环对集合遍历
              for (Object obj : coll){
                  System.out.println(obj);
              }
              // 2.增强for循环对普通数组遍历
              int[] arr = new int[]{1,2,3,4,5};
              for (int i : arr){
                  System.out.println(i);
              }
          }
      
    • 关于foreach的笔试题

      // 笔试题
      @Test
      	public void test1() {        
              // 新建数组str
      		String[] str = new String[]{"MM","MM","MM"};
      		// 1.用增强for循环为数组中元素赋值,赋值的是变量 s  ,所以原数组值不会变     
              for (String s : str){
                  s = "GG";
              }
              for (String t: str){
                  // 输出结果为:"MM","MM","MM"
                  System.out.println(t);	
              }
              // 2.用普通for循环为数组中元素赋值,赋值的就是数组元素,所以值会变
              for (int i = 0; i < str.length; i++) {
                  str[i] = "GG";
              }
              for (String r : str){
                  // 输出为:"GG","GG","GG"
                  System.out.println(r);	
              }
          }
      
(一) List接口
  1. 特点:

    ① 存储有序、可重复的数据
    ② “动态”数组,可动态调节长度,替换原有的数组

  2. 接口中的实现类

    ArrayList: 作为List接口的主要实现类,线程不安全、效率高;底层使用Object[ ] elementData存储
    LinkedList:对于频繁的插入、删除操作,效率高;底层使用双向链表存储
    Vector: 是List接口的旧实现类,线程安全,效率低;底层使用Object[ ] elementData存储

  3. ArrayList类的源码分析
    • jdk7的情况下:底层创建了长度是10的Object数组

      // ArrayList中的源码
      	// 1.空参构造器,底层创建了长度是10的Object数组:Object[] elementData
      	public ArrayList() {
              this = (10);
          }
      	// 2.add方法
      	public boolean add(E e) {
              // size:指elementData数组中已经添加的元素的个数        
      		ensureCapacityInternal(size + 1); 
              // transient Object[] elementData;是一个已经定义的Object空数组属性
      		elementData[size++] = e;
      		return true;
          }
      	
      	// 3.判断是否要扩容
          private void ensureExplicitCapacity(int minCapacity) {
              modCount++;
              // 
              if (minCapacity - elementData.length > 0)
                  grow(minCapacity);
          }
      	// 4.扩容方法
      	private void grow(int minCapacity) {
              // elementData数组中元素的个数作为旧的容量
              int oldCapacity = elementData.length;
              // 新的容量为旧的容量的1.5倍。>>1表示除以2
              int newCapacity = oldCapacity + (oldCapacity >> 1);
              // 如果新容量仍然不满足最小容量需求,则新容量变为最小容量需求
              if (newCapacity - minCapacity < 0)
                  newCapacity = minCapacity;
              // 如果新容量比类内定义的常量属性MAX_ARRAY_SIZE还大
              if (newCapacity - MAX_ARRAY_SIZE > 0)
                  // 则把整型的最大值赋给新容量。(如果超过整型最大值,就会抛异常)
                  newCapacity = hugeCapacity(minCapacity);
              // 把原先elementData数组拷贝到扩容后的新的elementData数组中
              elementData = Arrays.copyOf(elementData, newCapacity);
          }
      // ArrayList空参构造器,
      	
      	ArrayList list = new ArrayList()// elementData[0] = new Integer(123)    
      	list.add(123);	
      	···
          /* 
           如果此次的添加导致底层elementData数组容量不够,则扩容。
      	 默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中
      	*/
      	list.add(11);	
      	// 结论:建议开发中使用带参的构造器,减少因不断扩容带来的硬件开销
      	ArrayList list = new ArrayList(int capacity)
      
    • jdk8中的变化:底层创建了空的Object数组,首次调用add方法时才会扩容为10

      // ArrayList中的源码
      	// 1.空参构造器,底层创建了空的Object数组:Object[] elementData
      	public ArrayList() {
              // static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
              this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
          }
      	// 2.add方法
      	public boolean add(E e) {
              // size:指elementData数组中已经添加的元素的个数        
      		ensureCapacityInternal(size + 1); 
              // transient Object[] elementData;是一个已经定义的Object空数组属性
      		elementData[size++] = e;
      		return true;
          }
      	// 3.确定是否要扩容。  
      	private void ensureCapacityInternal(int minCapacity) {
              // minCapacity = size + 1
              ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
          }	
      	// 4.初次添加元素,就给elementData设置容量为10
      	private static int calculateCapacity(Object[] elementData, int minCapacity) {
              // 如果elementData数组为空,就返回默认容量10和添加元素个数+1之间的较大值
              if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                  // DEFAULT_CAPACITY = 10;
                  return Math.max(DEFAULT_CAPACITY, minCapacity);
              }
              return minCapacity;
          }
      	// 5.判断是否要继续扩容
          private void ensureExplicitCapacity(int minCapacity) {
              modCount++; 
              if (minCapacity - elementData.length > 0)
                  grow(minCapacity);
          }
      	// 6.扩容方法
      	private void grow(int minCapacity) {
              // elementData数组中元素的个数作为旧的容量
              int oldCapacity = elementData.length;
              // 新的容量为旧的容量的1.5倍。>>1表示除以2
              int newCapacity = oldCapacity + (oldCapacity >> 1);
              // 如果新容量仍然不满足最小容量需求,则新容量变为最小容量需求
              if (newCapacity - minCapacity < 0)
                  newCapacity = minCapacity;
              // 如果新容量比类内定义的常量属性MAX_ARRAY_SIZE还大
              if (newCapacity - MAX_ARRAY_SIZE > 0)
                  // 则把整型的最大值赋给新容量。(如果超过整型最大值,就会抛异常)
                  newCapacity = hugeCapacity(minCapacity);
              // 把原先elementData数组拷贝到扩容后的新的elementData数组中
              elementData = Arrays.copyOf(elementData, newCapacity);
          }
      
      	// 底层Object elementData初始化为{},并没有创建长度
      	ArrayList list = new ArrayList();
      	// 首次调用add(),底层才创建长度为10的数组,并将数据123添加到elementData[0]中
      	list.add(123);
      	// 后续的添加和扩容操作与jdk7无异
      	···	
      
    • 小结:

      • jdk7中的ArrayList的对象的创建类似于单例模式中的饿汉式,而jdk8中的ArrayList对象的创建类似于单例模式中的懒汉式,延迟了数组的创建,节省内存。
      • 建议开发中使用带参的构造器,减少因不断扩容带来的硬件开销
  4. LinkedList的源码分析
    // LinkedList源码
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        // Node定义:体现了LinkedList双向链表的说法
        Node(Node<E>) prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    	// 内部声明了Node类型的first和last属性,默认值为null
    	LinkedList list = new LinkedList();	
    	// 将123封装到Node中,创建了Node对象
    	list.add(123);
    
  5. List接口中的方法
    methodsummary
    void add( int index , Object ele )在index位置插入ele元素
    boolean addAll( int index , Collection eles)从index位置上将eles中的所有元素添加进来
    Object get( int index )获取指定位置上的元素
    int indexOf( Object obj )返回obj在集合中首次出现的位置,如不存在,返回-1
    int lastIndexOf( Object obj )返回obj在集合中最后出现的位置
    Object remove( int index )移除指定位置上的元素,并返回该元素
    Object set( int index , Object ele )将指定位置上的元素设定为ele
    List subList( int formIndex , int toIndex )返回从fromIndex到toIndex位置左闭右开的子集合
    @Test
        public void listTest() {
            // 新建ArrayList集合list1
            ArrayList list1 = new ArrayList();
            list1.add(123);
            list1.add(456);
            list1.add("AA");
            list1.add(new Person("Tom",21));
            list1.add(456);        
            // 集合list1的原本输出:[123, 456, AA, {Tom,21}, 456]
            System.out.println(list1);
    
            // 1.在集合索引0处插入元素
            list1.add(0,"first");
            // 输出结果为:[first, 123, 456, AA, {Tom,21}, 456]
            System.out.println(list1);
    
            // 2.1 新建List集合list2
            List list2 = Arrays.asList(11, 22, 33);
            // 2.2 在索引值3处插入该集合中的所有元素
            list1.addAll(3,list2);
            // 输出结果为:[first, 123, 456, 11, 22, 33, AA, {Tom,21}, 456]
            System.out.println(list1);
            
            // 3.得到集合索引值为0的元素
            Object obj = list1.get(0);
            // 输出为:123
            System.out.println(obj);
            
            // 4.返回元素456在集合中首次出现的索引值
            int index = list1.indexOf(456);
            // 输出为:1
            System.out.println(index);
            
            // 5.返回元素456在集合中最后一次出现的索引值
            int index1 = list1.lastIndexOf(456);
            // 输出为:3
            System.out.println(index1);
            
            
            // 6.删除索引值0处的元素,并将该元素返回
            Object obj1 = list1.remove(0);
            // 删除元素作为返回值返回:first
            System.out.println(obj1);
            // 删除后的集合输出:[123, 456, 11, 22, 33, AA, {Tom,21}, 456]
            System.out.println(list1);
            
            // 7.设置索引1处的元素为789
            Object obj2 = list1.set(1,789);
            // [123, 789, 11, 22, 33, AA, {Tom,21}, 456]
            System.out.println(list1);
            
            // 8.将索引值[2,4)范围内的元素组成一个新的集合并返回
            List list3 = list1.subList(2, 4);
            // 新集合输出:[11, 22]
            System.out.println(list3);
        }
    
(二) Set接口
  1. 特点:

    存储无序的、不可重复的数据 - - - > 高中时的“集合”

    ① **无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据元数据的哈希值决定排序的,第一个存储的数据可能不在数组的第一位,但是每个数据的存储位置时不变的
    ② ***不可重复:***保证添加的元素按照equals( )判断时,不能返回true。即:相同的元素只能添加一个
    ③ 添加的对象元素对应的类必须重写equals方法

  2. Set接口的框架

    HashSet:作为Set接口的主要实现类,线程不安全,可以存储null值

    | - - - LinkedHashSet:作为HashSet的子类,遍历内部数据时,可以按照添加的顺序遍历,因为增加了双向链表,常用于频繁遍历的集合

    TreeSet:使用对象排序接口,可以按照添加对象的指定属性,进行排序

  3. 添加元素的过程:以HashSet为例
    • 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode( )方法,计算元素a的哈希值

    • 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即:索引位置),判断数组在该位置上是否已经有其它元素:

      • 如果此位置没有其它元素,则元素a添加成功。 - - - > 情况1

      • 如果此位置有其它元素b(或以链表形式存在的多个元素),则比较元素a和元素b的hash值:

        ​ | - - - 如果hash值不相同,则元素 a 添加成功。 - - - > 情况2

        ​ | - - - 如果hash值相同,进而需要调用元素 a 所在类的equals( ) 方法:

        ​ | - - - equals( ) 返回false,则元素 a 添加成功 - - - > 情况3

        ​ | - - - equals( ) 返回true, 则元素 a 添加失败

      说明:

      • 底层也是数组,初始容量为16,超过容量,就会扩大容量为原来的2倍,结构为:数组 + 链表

      • 对于添加成功的情况2和情况3:虽然hash值不一样,但是对应的底层数组的索引位置可能是一样的,故元素a与已经存在指定索引位置上数据以链表的方式存储

      • 集合中的两个数据必须是hash值和equals值都相同,才能判定这两个元素是同一个数据

      • jdk7:元素 a 放到数组中,指向原来的元素

        jdk8:原来的元素在数组中,指向元素 a

  4. HashSet源码分析
    • 源码分析图(与HashMap源码分析图结合去看)

      在这里插入图片描述

    • 程序示例

      // HashSet测试类
      public class HashSetTest {
          public static void main(String[] args) {
              // 新建一个HashSet集合
              HashSet hashSet = new HashSet();
              hashSet.add(456);
              hashSet.add(456);
              hashSet.add(new Customer("Tom",24));
              hashSet.add(new Customer("Tom",24));
              hashSet.add("A");
      
              Iterator iterator = hashSet.iterator();
              while (iterator.hasNext()) {
                  System.out.println(iterator.next());
              }
          }
      
      }
      // 作为HashSet集合中对象数据对应的类,需要重写equals方法和hashCode方法
      class Customer {
          private String name;
          private  int age;
          public Customer() {}
          public Customer(String name, int age) {
              this.name = name;
              this.age = age;
          }
          public void setName(String name) {
              this.name = name;
          }
          public String getName() {
              return name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          @Override
          public String toString() {
              return "{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      
      /*	
      	1、下面重写的equals方法和hashCode方法是基于 IntelliJ Default
      	2、hashCode中的31的由来
      	   ① 选择系数的时候,要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的冲突就越少,查
               找起来效率也会提高。
             ② 31 只占用5bits,相乘造成数据溢出的概率较小
             ③ 31 可以由i*31 == (i<<5)-1 来表示,现在很多虚拟机中都有做相关优化,提高算法效率
             ④ 31 是一个素数,素数作用就是如果我用一个数字乘以这个素数,那么最终出来的结果只能被素数本
               身和被乘数还有1整除,减少冲突
      */
          @Override
          public boolean equals(Object o) {
              if (this == o) {
                  return true;
              }
              if (!(o instanceof Customer)) {
                  return false;
              }
      
              Customer customer = (Customer) o;
      
              if (getAge() != customer.getAge()) {
                  return false;
              }
              return getName().equals(customer.getName());
          }
      
          @Override
          public int hashCode() {
              int result = getName().hashCode();
              result = 31 * result + getAge();
              return result;
          }
      
      /*
      	这里调用的java.util.Objects.equals and hashCode(java 7+)中的重写方法
      	这里要区分Objects和Object
      		① Object中的hashCode方法的源码是C程序:public native int hashCode(); 
      		② Objects继承于Object,其hash方法调用了Arrays的hashCode()
      */
      	@Override
          public boolean equals(Object o) {
              if (this == o) {
                  return true;
              }
              if (!(o instanceof Customer)) {
                  return false;
              }
              Customer customer = (Customer) o;
              return getAge() == customer.getAge() && Objects.equals(getName(), customer.getName());
          }
      
          @Override
          public int hashCode() {
              return Objects.hash(getName(), getAge());
          }
      

      注:Object与Objects的区别

  5. LinkedHashSet存储

在这里插入图片描述

  1. TreeSet

    ① 向TreeSet中添加的数据,要求是相同类的对象,且对象对应的类必须排序接口之一,因为涉及到红黑树排序

    ② 两种排序方式:

    自然排序 (实现Comparable接口)

    比较两个对象是否相同的标准为compareTo( ) 返回0,不再是equals( )

    定制排序(实现Comparator接口

    比较两个对象是否相同的标准为compare( ) 返回0,不再是equals( )

    代码测试

    	@Test
    	    public void treeSetTest() {
    	        TreeSet set = new TreeSet();
    	        set.add(new Customer("Lily",36));
    	        set.add(new Customer("Alla",18));
    	        set.add(new Customer("Susan",19));
    	        set.add(new Customer("Susan",25));
    	        set.add(new Customer("Petty",79));
    	        set.add(new Customer("Joda",59));
    	
    	        Iterator iterator = set.iterator();
    	        while (iterator.hasNext()) {
    	            System.out.println(iterator.next());
    	        }
    	    }
    	class Custeomer implements Comparable{
    	    @Override
    	    public int compareTo(Object o) {
    	        if (o instanceof Customer) {
    	            Customer c = (Customer)o;
    	            int result = this.name.compareTo(c.name);
    	            if (result != 0) {
    	                return result;
    	            }
    	            if (result == 0) {
    	                return Integer.compare(this.age,c.age);
    	            }
    	        }
    	        throw new RuntimeException("输入的数据不正确");
    	    }
    	}
    
  2. 强调:
    • Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
    • 要求:向Set中添加数据,其所在的类一定要重写hashCode( ) 和 equals( ) 方法,实现对象相等规则。即:相等的对象必须具有相等的散列码,对象中用作 equals方法比较的 Field 都必须用来计算 hashCode。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

e_nanxu

感恩每一份鼓励-相逢何必曾相识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值