【JavaSE】集合(516~561)

news/2024/9/23 10:29:27/

516.集合-每天一考

1.什么是枚举类?枚举类的对象声明的修饰符都有哪些?

枚举类:类中的对象的个数是确定的,有限个。
private final (No)
public static final (Yes)

2.什么是元注解?说说Retention和Target元注解的作用

元注解:对现有的注解进行解释说明的注解。
Retention:指明所修饰的注解的生命周期。SOURCE CLASS RUNTIME

3.说说你所理解的集合框架都有哪些接口,存储数据的特点是什么

4.比较throw 和 throws 的异同

同:
throw:生成一个异常对象,并抛出。使用在方法内部 <-> 自动抛出异常对象
throws:处理异常的方式。使用在方法声明处的末尾<->try-catch-finally
“上游排污,下游治污”

5.谈谈你对同步代码块中同步监视器和共享数据的理解及各自要求。

同步监视器:俗称锁。
①任何一个类的对象都可以充当锁。
② 多个线程共用同一把锁。
共享数据:多个线程共同操作的数据,即为共享数据。
需要使用同步机制将操作共享数据的代码包起来。不能包多了,也不能包少了。

517.集合-复习:枚举类
518.集合-复习:注解
519.集合-复习:Collection

520.集合-Collection接口的常用方法2

作用方法
判断当前集合中是否包含objcontains(Object obj)
判断形参coll1中的所有元素是否都存在于当前集合中containsAll(Collection coll1)
从当前集合中移除obj元素remove(Object obj)
差集:从当前集合中移除coll1中所有的元素removeAll(Collection coll1)
交集:获取当前集合和coll1集合的交集,并返回给当前集合retainAll(Collection coll1)
要想返回true,需要当前集合和形参集合的元素都相同equals(Object obj)
返回当前对象的哈希值hashCode()
集合转换为数组集合.toArray()
数组转换为集合Arrays.asList(数组)
返回Iterator接口的实例,用于遍历集合元素iterator()

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

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;public class CollectionTest {@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);//Person p = new Person("Jerry",20);//coll.add(p);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//1.contains(Object obj):判断当前集合中是否包含obj//我们在判断时会调用obj对象所在类的equals()。boolean contains = coll.contains(123);System.out.println(contains);System.out.println(coll.contains(new String("Tom")));//System.out.println(coll.contains(p));//trueSystem.out.println(coll.contains(new Person("Jerry",20)));//false -->true//2.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));}    
}

521.集合-Collection接口的常用方法3

 @Testpublic void test2(){//3.remove(Object obj):从当前集合中移除obj元素。Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);coll.remove(1234);System.out.println(coll);//相当于调用toString方法coll.remove(new Person("Jerry",20));System.out.println(coll);//4. removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素。Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);}@Testpublic void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//5.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
//        Collection coll1 = Arrays.asList(123,456,789);
//        coll.retainAll(coll1);
//        System.out.println(coll);//6.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同。Collection coll1 = new ArrayList();coll1.add(456);coll1.add(123);coll1.add(new Person("Jerry",20));coll1.add(new String("Tom"));coll1.add(false);System.out.println(coll.equals(coll1));}

522.集合-Collection接口的常用方法4

	@Testpublic void test4(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//7.hashCode():返回当前对象的哈希值System.out.println(coll.hashCode());//8.集合 --->数组:toArray()Object[] arr = coll.toArray();for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}//拓展:数组 --->集合:调用Arrays类的静态方法asList()List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});System.out.println(list);List arr1 = Arrays.asList(new int[]{123, 456});//会出现将new int[]{123, 456}当成一个元素的问题System.out.println(arr1.size());//1List arr2 = Arrays.asList(new Integer[]{123, 456});//Integer会识别成两个元素System.out.println(arr2.size());//2//9.iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试}

523.集合-使用Iterator遍历Collection

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

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

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

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

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

package com.atguigu.java;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 {@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);Iterator iterator = coll.iterator();//方式一:演示,不能这么写
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        //调用超范围之后报异常:NoSuchElementException
//        System.out.println(iterator.next());//方式二:不推荐
//        for(int i = 0;i < coll.size();i++){
//            System.out.println(iterator.next());
//        }//方式三:推荐//hasNext():判断是否还有下一个元素while(iterator.hasNext()){//next():①指针下移 ②将下移以后集合位置上的元素返回System.out.println(iterator.next());}}    
}

524.集合-迭代器Iterator的执行原理

1.iterator指向第一个对象的上面,为空的位置
2.iterator.hasNext,判断iterator的后一个位置有没有元素,有返回true
3.调用next,先指针下移,返回下移以后位置上的元素

在这里插入图片描述

在这里插入图片描述

525.集合-Iterator遍历集合的两种错误写法

	@Testpublic void test2(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//错误方式一:iterator.next()判断时造成了指针下移
//        Iterator iterator = coll.iterator();
//        while((iterator.next()) != null){
//            System.out.println(iterator.next());
//        }//错误方式二://集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。while (coll.iterator().hasNext()){System.out.println(coll.iterator().next());}}

526.集合-Iterator迭代器remove()的使用

注意:
Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方 法,不是集合对象的remove方法。

如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。

	//测试Iterator中的remove()//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,// 再调用remove都会报IllegalStateException。@Testpublic void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//删除集合中"Tom"Iterator iterator = coll.iterator();while (iterator.hasNext()){//iterator.remove();//在next之前先调remove报异常Object obj = iterator.next();if("Tom".equals(obj)){iterator.remove();}}//遍历集合//指针已经下移,必须重新得到iterator对象iterator = coll.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}

527.集合-新特性foreach循环遍历集合或项目

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

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

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

foreach还可以用来遍历数组。

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
//jdk 5.0 新增了foreach循环,用于遍历集合、数组
public class ForTest {@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//for(集合coll元素的类型 局部变量(随便指定) : coll集合对象)//内部仍然调用了迭代器。//循环从coll中取元素赋值给objfor(Object obj : coll){System.out.println(obj);}}@Testpublic void test2(){int[] arr = new int[]{1,2,3,4,5,6};//for(数组元素的类型 局部变量 : 数组对象)for(int i : arr){System.out.println(i);}}//练习题@Testpublic void test3(){String[] arr = new String[]{"MM","MM","MM"};//        //方式一:普通for赋值重新赋值
//        for(int i = 0;i < arr.length;i++){
//            arr[i] = "GG";
//        }//方式二:增强for循环,重新赋值的是s,arr不变for(String s : arr){s = "GG";//打印还全是MM}for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}}
}

528.集合-List接口常用实现类的对比

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

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

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

JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。

List接口框架
Collection接口:单列集合,用来存储一个一个的对象
List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原有的数组
ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
相同点:ArrayList、LinkedList、Vector三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据

529.集合-ArrayList的源码分析

jdk 7情况下

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)//capacity大小

jdk 8中ArrayList的变化

ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
//...
//后续的添加和扩容操作与jdk 7 无异。

小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

在这里插入图片描述

530.集合-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;}
}

531.集合-Vector源码分析

jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。

在扩容方面,默认扩容为原来的数组长度的2倍

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

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

532.集合-List接口中的常用方法测试

方法作用
add(Object obj)
remove(int index) / remove(Object obj)
set(int index, Object ele)
get(int index)
add(int index, Object ele)
size()长度
Iterator迭代器方式、增强for循环、普通的循环遍历
void add(int index, Object ele)在index位置插入ele元素
boolean addAll(int index, Collection eles)从index位置开始将eles中的所有元素添加进来
Object get(int index)获取指定index位置的元素
int indexOf(Object obj)返回obj在集合中首次出现的位置,不存在返回-1
int lastIndexOf(Object obj)返回obj在当前集合中末次出现的位置
Object remove(int index)移除指定index位置的元素,并返回此元素
Object set(int index, Object ele)设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex)返回从fromIndex到toIndex位置的子集合
	@Testpublic void test1(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");list.add(new Person("Tom",12));list.add(456);System.out.println(list);//void add(int index, Object ele):在index位置插入ele元素list.add(1,"BB");System.out.println(list);//boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来List list1 = Arrays.asList(1, 2, 3);list.addAll(list1);
//        list.add(list1);//把list1整体当成一个元素了,会返回7System.out.println(list.size());//6+3=9//Object get(int index):获取指定index位置的元素System.out.println(list.get(0));}@Testpublic void test2(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");list.add(new Person("Tom",12));list.add(456);//int indexOf(Object obj):返回obj在集合中首次出现的位置。如果不存在,返回-1.int index = list.indexOf(4567);System.out.println(index);//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置。如果不存在,返回-1.System.out.println(list.lastIndexOf(456));//Object remove(int index):移除指定index位置的元素,并返回此元素Object obj = list.remove(0);System.out.println(obj);System.out.println(list);//Object set(int index, Object ele):设置指定index位置的元素为elelist.set(1,"CC");System.out.println(list);//List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合List subList = list.subList(2, 4);System.out.println(subList);System.out.println(list);}

533.集合-List遍历及方法总结

public class ListTest {@Testpublic void test3(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");//方式一:Iterator迭代器方式Iterator iterator = list.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("***************");//方式二:增强for循环for(Object obj : list){System.out.println(obj);}System.out.println("***************");//方式三:普通for循环for(int i = 0;i < list.size();i++){System.out.println(list.get(i));}}    
}

534.集合-List的一个面试小题

ArrayList和LinkedList的异同
二者都线程不安全,相对线程安全的Vector,执行效率高。
此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于 随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增 和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

区分List中remove(int index)和remove(Object obj)

import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class ListExer {@Testpublic void testListRemove() {List list = new ArrayList();list.add(1);list.add(2);list.add(3);updateList(list);System.out.println(list);//list.remove(2)时删除位置2的3,返回[1,2]//list.remove(new Integer(2))删除数据2,返回[1,3]}private void updateList(List list) {
//        list.remove(2);list.remove(new Integer(2));}
}

535.集合-Set接口实现类的对比

Set接口是Collection的子接口,set接口没有提供额外的方法。Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。

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

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

Set接口的框架:
Collection接口:单列集合,用来存储一个一个的对象
Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet:可以按照添加对象的指定属性,进行排序。

HashSet
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。

HashSet 具有以下特点:
不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null
HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。

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

536.集合-Set的无序性与不可重复性的理解

Set:存储无序的、不可重复的数据
以HashSet为例说明:
1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
2.不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个。

	@Testpublic void test1(){Set set = new HashSet();set.add(456);set.add(123);set.add(123);set.add("AA");set.add("CC");set.add(new User("Tom",12));//直接执行有两个Tom,User中只重写equals时还是有两个set.add(new User("Tom",12));//User中同时重写hashcode时有一个set.add(129);Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}

537.集合-HashSet中元素的添加过程

向HashSet中添加元素的过程:

  1. 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法 来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象 在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
  2. 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过 链表的方式继续链接。
  3. 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

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

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。

538.集合-关于hashCode()和equals()的重写

要求:
1.向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
2.重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
3.重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

重写 hashCode() 方法的基本原则
1.在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
2.当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。
3.对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

重写 equals() 方法的基本原则
以自定义的Customer类为例,何时需要重写equals()?
1.当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是
要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。
2.因此,违反了“相等的对象必须具有相等的散列码”
3.结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算

在这里插入图片描述

539.集合-LinkedHashSet的使用

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

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

LinkedHashSet 不允许集合元素重复

LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。

优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

import org.junit.Test;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;public class SetTest {@Testpublic void test2(){Set set = new LinkedHashSet();set.add(456);set.add(123);set.add(123);set.add("AA");set.add("CC");set.add(new User("Tom",12));set.add(new User("Tom",12));set.add(129);Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}

在这里插入图片描述

540.集合-TreeSet的自然排序

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

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

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

在这里插入图片描述

红黑树具体参考:http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html

在这里插入图片描述
在这里插入图片描述

import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {/*1.向TreeSet中添加的数据,要求是相同类的对象。2.两种排序方式:自然排序(实现Comparable接口) 和定制排序(Comparator)3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().4.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().*/@Testpublic void test1(){TreeSet set = new TreeSet();//失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new User("Tom",12));//举例一:从小到大的顺序
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);//举例二:自定义的对象user需要实现接口,自然排序(实现Comparable接口) 和定制排序(Comparator)set.add(new User("Tom",12));set.add(new User("Jerry",32));set.add(new User("Jim",2));set.add(new User("Mike",65));set.add(new User("Jack",33));//判断是否相同拿Comparable进行比较,返回是0就认为相同,不是拿equals进行比较set.add(new User("Jack",56));//想添加两个Jack可以指定二级排序Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}    
}

541.集合-TreeSet的定制排序

TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没 有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照 其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来 实现。需要重写compare(T o1,T o2)方法。

利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。

要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。

此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异 常。

使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

	@Testpublic void test2(){Comparator com = new Comparator() {//按照年龄从小到大排列@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof User && o2 instanceof User){User u1 = (User)o1;User u2 = (User)o2;return Integer.compare(u1.getAge(),u2.getAge());}else{throw new RuntimeException("输入的数据类型不匹配");}}};TreeSet set = new TreeSet(com);set.add(new User("Tom",12));set.add(new User("Jerry",32));set.add(new User("Jim",2));set.add(new User("Mike",65));set.add(new User("Mary",33));set.add(new User("Jack",33));set.add(new User("Jack",56));Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}

542.集合-每天一考

1.集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法?为什么?

equals()方法。 contains() /remove()/retainsAll() ….
List:equals()方法
Set:
(HashSet、LinkedHashSet为例):equals()、hashCode(),不能添加重复数据
(TreeSet为例):Comparable:compareTo(Object obj)
Comparator:compare(Object o1,Object o2)

2.ArrayList,LinkedList,Vector三者的相同点与不同点?

List Map Set

3.List 接口的常用方法有哪些?(增、删、改、查、插、长度、遍历)

add(Object obj)
remove(Object obj)/remove(int index)
set(int index,Object obj)
get(int index)
add(int index,Object obj)
size()
使用Iterator;foreach;普通的for

4.如何使用Iterator和增强for循环遍历List。举例说明

5.Set存储数据的特点是什么?常见的实现类有什么?说明一下彼此的特点。

HashSet LinkedHashSet TreeSet
HashMap LinkedHashMap TreeMap

543.集合-复习:Collection及Collection的遍历
544.集合-复习:List接口
545.集合-复习:Set接口

546.集合-TreeSet的课后练习

package com.atguigu.exer1;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;/*** 创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(下一章:TreeSet 需使用泛型来定义)分别按以下两种方式对集合中的元素进行排序,并遍历输出:1). 使Employee 实现 Comparable 接口,并按 name 排序2). 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。*/
public class EmployeeTest {//问题二:按生日日期的先后排序。@Testpublic void test2(){TreeSet set = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Employee && o2 instanceof Employee){Employee e1 = (Employee)o1;Employee e2 = (Employee)o2;MyDate b1 = e1.getBirthday();MyDate b2 = e2.getBirthday();//方式一:
//                    //比较年
//                    int minusYear = b1.getYear() - b2.getYear();
//                    if(minusYear != 0){
//                        return minusYear;
//                    }
//                    //比较月
//                    int minusMonth = b1.getMonth() - b2.getMonth();
//                    if(minusMonth != 0){
//                        return minusMonth;
//                    }
//                    //比较日
//                    return b1.getDay() - b2.getDay();//方式二:return b1.compareTo(b2);}
//                return 0;throw new RuntimeException("传入的数据类型不一致!");}});Employee e1 = new Employee("liudehua",55,new MyDate(1965,5,4));Employee e2 = new Employee("zhangxueyou",43,new MyDate(1987,5,4));Employee e3 = new Employee("guofucheng",44,new MyDate(1987,5,9));Employee e4 = new Employee("liming",51,new MyDate(1954,8,12));Employee e5 = new Employee("liangzhaowei",21,new MyDate(1978,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}//问题一:使用自然排序@Testpublic void test1(){TreeSet set = new TreeSet();Employee e1 = new Employee("liudehua",55,new MyDate(1965,5,4));Employee e2 = new Employee("zhangxueyou",43,new MyDate(1987,5,4));Employee e3 = new Employee("guofucheng",44,new MyDate(1987,5,9));Employee e4 = new Employee("liming",51,new MyDate(1954,8,12));Employee e5 = new Employee("liangzhaowei",21,new MyDate(1978,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}
}

547.集合-Set课后两道面试题

import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class CollectionTest {@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(343);coll.add(343);coll.forEach(System.out::println);}//练习:在List内去除重复数字值,要求尽量简单public static List duplicateList(List list) {HashSet set = new HashSet();set.addAll(list);return new ArrayList(set);}@Testpublic void test2(){List list = new ArrayList();list.add(new Integer(1));list.add(new Integer(2));list.add(new Integer(2));list.add(new Integer(4));list.add(new Integer(4));List list2 = duplicateList(list);for (Object integer : list2) {System.out.println(integer);}}@Testpublic void test3(){HashSet set = new HashSet();Person p1 = new Person(1001,"AA");Person p2 = new Person(1002,"BB");set.add(p1);set.add(p2);System.out.println(set);p1.name = "CC";set.remove(p1);System.out.println(set);set.add(new Person(1001,"CC"));System.out.println(set);set.add(new Person(1001,"AA"));System.out.println(set);}
}

548.集合-Map接口及其多个实现类的对比

Map:双列数据,存储Key-value对的数据,类似与高中的函数:y=f(x)
在这里插入图片描述
在这里插入图片描述

549.集合-Map中存储的key-value的特点

在这里插入图片描述
在这里插入图片描述

550.集合-HashMap在JDK7中的底层实现原理

HashMap的底层实现原理?以jdk7为例说明:
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
可能已经执行过多次put…
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值(非常大)经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
如果equals()返回false:此时key1-value1添加成功。----情况3
如果equals()返回true:使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
在这里插入图片描述
在这里插入图片描述

551.集合-HashMap在JDK8中的底层实现原理 jdk8 相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk 8底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
    4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
    4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。

552.集合-HashMap在JDK7中的源码分析

在这里插入图片描述
新建Entry数组

 public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);// Find a power of 2 >= initialCapacityint capacity = 1;while (capacity < initialCapacity)capacity <<= 1;this.loadFactor = loadFactor;//loadFactor 加载因子0.75//threshold影响何时扩容threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);//16*0.75=12table = new Entry[capacity];//capacity=16useAltHashing = sun.misc.VM.isBooted() &&(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);init();}
public V put(K key, V value) {if (key == null)return putForNullKey(value);//计算当前key的hash值int hash = hash(key);//通过hash计算在数组中的存放位置int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}
void addEntry(int hash, K key, V value, int bucketIndex) {//size >= 12 && 将要放的位置不空if ((size >= threshold) && (null != table[bucketIndex])) {//扩容为原来的二倍resize(2 * table.length);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}createEntry(hash, key, value, bucketIndex);}

553.集合-HashMap在JDK8中的源码分析

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;//当table为null时帮忙造好if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}
final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;}

554.集合-LinkedHashMap底层实现

 *  //LinkedHashMap的底层实现原理(了解)*      源码中:*      static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;//能够记录添加的元素的先后顺序Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}*

555.集合-Map中的常用方法1

Map中定义的方法:
添加、删除、修改操作:
Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据

元素查询的操作:
Object get(Object key):获取指定key对应的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等

556.集合-Map中的常用方法2

元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合

557.集合-TreeMap两种添加方式的使用

在这里插入图片描述

558.集合-Properties处理文件属性

在这里插入图片描述

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;public class PropertiesTest {//Properties:常用来处理配置文件。key和value都是String类型public static void main(String[] args)  {FileInputStream fis = null;try {Properties pros = new Properties();fis = new FileInputStream("jdbc.properties");pros.load(fis);//加载流对应的文件String name = pros.getProperty("name");String password = pros.getProperty("password");System.out.println("name = " + name + ", password = " + password);} catch (IOException e) {e.printStackTrace();} finally {if(fis != null){try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}
}

559.集合-Collections工具类常用方法的测试

在这里插入图片描述
在这里插入图片描述

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** Collections:操作Collection、Map的工具类*** 面试题:Collection 和 Collections的区别?**/
public class CollectionsTest {/*
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值*/@Testpublic void test2(){List list = new ArrayList();list.add(123);list.add(43);list.add(765);list.add(-97);list.add(0);//报异常:IndexOutOfBoundsException("Source does not fit in dest")
//        List dest = new ArrayList();
//        Collections.copy(dest,list);//正确的:List dest = Arrays.asList(new Object[list.size()]);System.out.println(dest.size());//list.size();Collections.copy(dest,list);System.out.println(dest);/*Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题*///返回的list1即为线程安全的ListList list1 = Collections.synchronizedList(list);}@Testpublic void test1(){List list = new ArrayList();list.add(123);list.add(43);list.add(765);list.add(765);list.add(765);list.add(-97);list.add(0);System.out.println(list);//        Collections.reverse(list);
//        Collections.shuffle(list);
//        Collections.sort(list);
//        Collections.swap(list,1,2);int frequency = Collections.frequency(list, 123);System.out.println(list);System.out.println(frequency);}
}

560.集合-集合课后几道练习题说明
561.集合-Java版数据结构简述


http://www.ppmy.cn/news/387591.html

相关文章

516511

阿斯顿撒自行车墨迹墨迹快慢结合

ClickHouse exception code:516

当客户端连接clickhouse-server出现异常&#xff1a;ClickHouse exception, code: 516 &#xff0c; 经过分析错误码&#xff1a; ClickHouse exception, code: 516, host: 192.168.0.108, port: 8888; Code: 516, e.displayText() DB::Exception: default: Authentication f…

Leetcode.516 最长回文子序列

题目链接 Leetcode.516 最长回文子序列 题目描述 给你一个字符串 s&#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列。 示例 1&#x…

【LeetCode-516】516.最长回文子序列(给定一个字符串s,找到其中最长的回文子序列,并返回该序列的长度。可以假设s的最大长度为1000。)

516.最长回文子序列 给定一个字符串s&#xff0c;找到其中最长的回文子序列&#xff0c;并返回该序列的长度。可以假设s的最大长度为1000。 解题思路&#xff1a;动态规划 超赞的解题思路 dp 数组的定义是&#xff1a;在子串 s[i…j] 中&#xff0c;最长回文子序列的长度为…

Apifox:详细使用教程,带你轻松拿捏

目录 Apifox简介 Apifox的安装与使用 Apifox新建项目的流程 编写接口文档 Apifox简介 我们在日常编程开发过程中经常实行的是前后端分离架构的模式&#xff0c;一个项目的落地会通过产品、开发、测试三方会审&#xff0c;对项目需求评审过后&#xff0c;前后端开发会制定一些接…

Navicat 受邀出席 PostgreSQL 技术峰会,欢迎莅临我们的展台了解 Navicat 工具包如何提升你的工作效能

Navicat 受邀出席 PostgreSQL 技术峰会成都站&#xff0c;欢迎童鞋们莅临我们的展台。你有机会与我们的专家面对面交流&#xff0c;并了解实用的 Navicat 工具包如何帮助PostgreSQL用户&#xff08;应用开发人员、DBA、运维人员以及数据分析师&#xff09;有效地提升日常的工作…

19.白盒测试逻辑覆盖有哪几种覆盖标准,覆盖率最高的是什么?

语句覆盖&#xff1a;设计若干测试用例&#xff0c;运行被测程序&#xff0c;使程序中每个可执行语句至少执行一次 优点&#xff1a;可以直观的从源代码得到测试用例缺点&#xff1a;仅仅针对程序逻辑中显式存在的语句&#xff0c;无法针对隐藏的条件进行测试。语句覆盖是最弱…

【MarkerDown】CSDN Markdown之流程图graphflowchart详解

基本语法 flowchart/graph 流程图&#xff08;Flowcharts/Graphs&#xff09;是由节点 (几何形状) 和连接线 (箭头或线条)组成的. Mermaid代码定义了节点和连线的编码方式&#xff0c;并支持不同的箭头类型、多向箭头以及子图之间的任意链接。 警告 如果在流程图的节点使用e…