集合一看就会,一问就废

根着尚硅谷老师java视频做的笔记

集合

Java集合框架概述

  1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器.

    说明:此时的存储,主要是指的是内存层面的存储,不涉及持久化存储(.txt,.jpg,.avi, 数据库中)

  2. 数组存储多个数据方面的特点

    • 2.1特点

    一旦初始化以后,长度就确定了

    数组一旦定义好,其元素的类型也确定了.我们也就只能操作指定类型的数据了,比如:String[] arr; int[] arr1; Object[] arr2;(Object可以装他的子类)

    • 2.2缺点

    一旦初始化,长度就不能修改了

    数组中提供的方法有限,对于添加、删除、插入数据等操作,非常不便,效率不高

    获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用

    数组存储数据的特点:有序、重复.对于无序、不可重复的需求,不能满足;

  • Collection接口继承树(虚线代表实现 实线代表继承关系)

在这里插入图片描述

  • Map接口实现树

在这里插入图片描述

|----Collection接口:单例集合,用来存储一个一个对象

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

​ |----ArrayList、LinkList、Vector

​ |----Set接口:存储无序的、不可重复的数据 -->高中讲的"集合"

​ |----HashSet、LinkedHashSet、TreeSet

|----Map接口:双列集合,用来存储一对(key-value)一对的数据 —>高中函数:y=f(x)

​ |----HashMap、LinkedHashMap、TreeMap、HashTable、Properties

Collection接口方法的使用

booleanadd(E e) 确保此集合包含指定的元素(可选操作)。
booleanaddAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合(可选操作)。
voidclear() 从此集合中删除所有元素(可选操作)。
booleancontains(Object o) 如果此集合包含指定的元素,则返回 true
booleancontainsAll(Collection<?> c) 如果此集合包含指定 集合中的所有元素,则返回true。
booleanequals(Object o) 将指定的对象与此集合进行比较以获得相等性。
inthashCode() 返回此集合的哈希码值。
booleanisEmpty() 如果此集合不包含元素,则返回 true
Iterator<E>iterator() 返回此集合中的元素的迭代器。
default Stream<E>parallelStream() 返回可能并行的 Stream与此集合作为其来源。
booleanremove(Object o) 从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
booleanremoveAll(Collection<?> c) 删除指定集合中包含的所有此集合的元素(可选操作)。
default booleanremoveIf(Predicate<? super E> filter) 删除满足给定谓词的此集合的所有元素。
booleanretainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(可选操作)。
intsize() 返回此集合中的元素数。
default Spliterator<E>spliterator() 创建一个Spliterator在这个集合中的元素。
default Stream<E>stream() 返回以此集合作为源的顺序 Stream
Object[]toArray() 返回一个包含此集合中所有元素的数组。
<T> T[]toArray(T[] a) 返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
  • 代码测试
package SetBao;

import org.junit.Test;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

public class CollectionTest {
    @Test
    public void test1(){
        //Collection是接口,所有用ArrayList去测试他接口中定义的抽象方法的具体功能
        Collection coll = new ArrayList();
        //add(Object e) :将元素e添加到集合coll中
        coll.add("AA");
        coll.add("BB");
        coll.add(123);  //自动装箱
        coll.add(new Date());

        //size():获取添加的元素的个数
        System.out.println(coll.size()); //4

        //addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("CC");
        coll.addAll(coll1);
        coll.addAll(coll1);
        System.out.println(coll.size()); //6
        System.out.println(coll.toString()); //调ArrayList集合中重写的toString
        //clear():清空集合元素
        coll.clear();
        //isEmpty():判断当前集合是否为空
        System.out.println(coll.isEmpty());

    }
}
结果:
4
8
[AA, BB, 123, Mon May 10 15:06:59 CST 2021, 456, CC, 456, CC]
true
  • Person类
package SetBao;

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

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = 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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 方法测试1
 @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);  //自动装箱变成对象
        coll.add(456);  //自动装箱变成对象
        coll.add(new String("Tom"));
        coll.add(false);
//        Person p = new Person("Jerry", 20);
        coll.add(new Person("Jerry", 20));
        //1.contains(Object obj):判断当前集合是否包含obj
        //我们在判断时会调用集合中对象所在类的equals()
        boolean contains=coll.contains(123);
        System.out.println(contains);  //true
        System.out.println(coll.contains(new String("Tom")));  //String重写了equals
        System.out.println(coll.contains(new Person("Jerry", 20)));//Person没有重写equals,走的是Object的方法
        //2.containAll(Collection coll1):判断形参coll1中的所有元素是否都在当前集合中
        Collection coll1 = Arrays.asList(123,456,new String("Tom"));
        boolean b = coll.containsAll(coll1);
        System.out.println(b);//true

        Collection coll2 = new ArrayList();
        coll2.add(123);  //自动装箱变成对象
        coll2.add(456);  //自动装箱变成对象
        coll2.add(new String("Tom"));
        boolean b1 = coll.containsAll(coll2);
        System.out.println(b1);//true
    }
结果:
true
true
false
true
true
  • 测试方法2
@Test
public void test2(){
    //3.remove(Object obj):从当前集合中删除obj元素
    Collection coll = new ArrayList();
    coll.add(123);  //自动装箱变成对象
    coll.add(456);  //自动装箱变成对象
    coll.add(new String("Tom"));
    coll.add(false);
    coll.add(new Person("Jerry", 20));
    boolean c = coll.remove(123);//也会调用equals去找看有没有
    System.out.println(c);//true  移除成功
    System.out.println(coll);//[456, Tom, false, Person{name='Jerry', age=20}]
    coll.remove(new String("Tom"));
    System.out.println(coll);//[456, false, Person{name='Jerry', age=20}]
    boolean b2 = coll.remove(new Person("Jerry", 20));//Person重写equals()后才会移除成功
    System.out.println(coll);//[456, false, Person{name='Jerry', age=20}]
    System.out.println(b2);//false;
    //4.removeAll(Collection coll):差集:从当前集合中移除coll1中所有元素(即移除交集部分)
    Collection coll1=Arrays.asList(456,789);
    coll.removeAll(coll1);
    System.out.println(coll);//[false, Person{name='Jerry', age=20}]
}
结果:
true
[456, Tom, false, Person{name='Jerry', age=20}]
[456, false, Person{name='Jerry', age=20}]
[456, false, Person{name='Jerry', age=20}]
false
[false, Person{name='Jerry', age=20}]

Process finished with exit code 0

  • 方法测试3
@Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);  //自动装箱变成对象
        coll.add(456);  //自动装箱变成对象
        coll.add(new String("Tom"));
        coll.add(false);



//        Collection coll1=Arrays.asList(456,789);
        //5. retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合(应该说修改coll集合)
//        boolean b = coll.retainAll(coll1);
//        System.out.println(b);  //true  返回的都是true,只要能求交集,不管有没有元素,返回都是true
//        System.out.println(coll); //[456]

        //6.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同
        Collection coll1 = new ArrayList();
        coll1.add(456);  //自动装箱变成对象

        coll1.add(123);  //自动装箱变成对象
        coll1.add(new String("Tom"));
        coll1.add(false);

        System.out.println(coll.equals(coll1)); //位置必须也一样,是有序的,因为右边是ArrayList

    }
结果:
false
  • 测试4
@Test
public void test4(){
    Collection coll1 = new ArrayList();
    coll1.add(456);  //自动装箱变成对象

    coll1.add(123);  //自动装箱变成对象
    coll1.add(new String("Tom"));
    coll1.add(false);
    //7.hasCode():返回当前对象的哈希值
    System.out.println(coll1.hashCode());
    //8.集合转换为--->数组:toArray()
    coll1.toArray();
    Object[] arr=coll1.toArray();
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
    System.out.println("++++++++++++++++++");
    //拓展:数组--->集合:调用Arrays类的静态方法asList()
    List<Object> objects = Arrays.asList(new Object[]{"AA", "BB", "CC"});
    List<String> s = Arrays.asList(new String[]{"AA", "BB", "CC"});
    System.out.println(objects);//[AA, BB, CC]
    System.out.println(s);//[AA, BB, CC]

    List arr1= Arrays.asList(new int[]{123,456});
    System.out.println(arr1.size()); //1 他认为上面的是一个元素
    List arr2= Arrays.asList(new Integer[]{123,456});
    System.out.println(arr2.size());  //2   包装类的时候就可以被认为是两个元素
}
结果:
17240151
456
123
Tom
false
++++++++++++++++++
[AA, BB, CC]
[AA, BB, CC]
1
2

  • 方法测试iterator():返回Iterator接口的实例,用于遍历集合元素,放在IteratorTest.Java中

Iterator迭代器接口

在这里插入图片描述

  • 概述:

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

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

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

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

  • 内部方法 :hasNext()和next()

  @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);  //自动装箱变成对象
        coll.add(456);  //自动装箱变成对象
        coll.add(new String("Tom"));
        coll.add(false);
        coll.add(new Person("Jerry", 20));
        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());
//        }
        //方式三:推荐
        while ((iterator.hasNext())){
            System.out.println(iterator.next());
        }
    }
结果:
123
456
Tom
false
Person{name='Jerry', age=20}
  • 错误用法
   @Test
    public void test2(){
        Collection coll = new ArrayList();
        coll.add(123);  //自动装箱变成对象
        coll.add(456);  //自动装箱变成对象
        coll.add(new String("Tom"));
        coll.add(false);
        coll.add(new Person("Jerry", 20));

        //错误方式一:NoSuchElementException
        Iterator iterator = coll.iterator();
//        while (iterator.next()!=null){
//            System.out.println(iterator.next());
//        }
//        //错误   不断出现123
//        while (coll.iterator().hasNext()){
//            System.out.println(coll.iterator().next());
//        }
    }
  • 测试Iterator的remove(),可以在遍历的时候,删除集合中的元素,此方法不同于集合直接调用remove() ,但两者都是真正把元素在coll中移除了.
@Test
public void test3(){
    Collection coll = new ArrayList();
    coll.add(123);  //自动装箱变成对象
    coll.add(456);  //自动装箱变成对象
    coll.add(new String("Tom"));
    coll.add(false);
    coll.add(new Person("Jerry", 20));

    //删除集合中"Tom"
    Iterator iterator = coll.iterator();
    while (iterator.hasNext()){
        Object object = iterator.next();
        //删除集合中
        if("Tom".equals(object))
            iterator.remove();
    }
    System.out.println("=========重新遍历=======");
    Iterator iterator1 = coll.iterator();
    while (iterator1.hasNext()){
        System.out.println(iterator1.next());
    }
    System.out.println(coll);

}
  • 注意一些细节:

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

    remove都会报IllegaLStateException.

foreach

  • jdk5.0新增了foreach循环,用于遍历集合、数组

  • 遍历集合

@Test
public void test1(){
    Collection coll = new ArrayList();
    coll.add(123);  //自动装箱变成对象
    coll.add(456);  //自动装箱变成对象
    coll.add(new String("Tom"));
    coll.add(false);
    coll.add(new Person("Jerry", 20));
    //for(集合中元素类型  局部变量: 集合对象
    //内部仍然调用了迭代器
    for (Object obj: coll ) {
        System.out.println(obj);
    }
}
结果:
123
456
Tom
false
Person{name='Jerry', age=20}
  • 遍历数组
@Test
public void test2(){
	
    int[] arr=new int[]{1,2,3,4,5};
    //for(数组元素的类型 局部变量:数组对象)
    for (int i:arr) {
        System.out.println(i);
    }
}
结果:
1
2
3
4
5
  • 练习题
@Test
public void test3(){
    String [] arr=new String[]{"MM","MM","MM"};
    //方式一:普通for循环
    for (int i = 0; i < arr.length; i++) {
        arr[i]="GG";
    }
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);  //GG
    }
    //方式二:增强for循环
    for (String s:arr
         ) {
        s="MM";  //改的是s,没有该arr里面的,重新赋s的值
    }
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
}
结果:
GG
GG
GG
GG
GG
GG

Collection子接口一:List

概述

List接口的三个实现类

​ ArrayList LinkedList Vector

面试题:Arrayay、LinkedList、Vector三者的异同

List(1.2出现)的实现类相同点
ArrayList实现List接口、存储有序的、可重复的数据
LinkedList实现List接口、存储有序的、可重复的数据
Vector实现List接口、存储有序的、可重复的数据

|----Collection接口:单例集合,用来存储一个一个对象

​ |----List(1.2才有)接口:存储有序的、可重复的数据 -->"动态"数组

​ |----ArrayLis:作为list接口的主要实现类:线程不安全的、效率高;底层使用Object[]存储 ,源码:transient Object[] elementData; // non-private to simplify nested class access

​ |----LinkList:对于频繁的插入、删除操作、使用此类效率比ArrayList高;底层使用双向链表存储.

​ |----Vector:作为List接口的古老实现类(1.0就有);线程安全,效率低;底层使用Object[]存储 源码:protected Object[] elementData;

  • LinkList底层源码(用双向链表存储数据)
/**
 * Pointer to first node.  //指向第一个结点
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * Pointer to last node.  //指向最后个节点
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;
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源码分析
  • JDK7的ArrayList源码

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无异

  1. 调用空参构造器的时候并没有把数组实例化,而JDK7调用空参构造器的时候直接实例化了,构建数组的数组长度是10,一定程度上浪费了内存
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  1. 调用add方法的时候 ,会先去看数组长度够不够
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) { //第一次add时候 minCapacity=1
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
  • 如果elementData=={},那就返回DEFAULT_CAPACITY, minCapacity最大值,就是10,不空就返回minCapacity,然后返回的值作为 ensureExplicitCapacity()括号里面的参数
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  //DEFAULTCAPACITY_EMPTY_ELEMENTDATA)={}
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
  • 然后 ensureExplicitCapacity()判断当前值是不是大于当前数组大小,大于就进行grow扩容,不大于什么也不做.
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • 扩容大小和jdk7一样也是原来的1.5倍,grow(int minCapacity)
/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *增加这容量确保他可以容纳至少这期望的最小容纳数量
     * @param minCapacity the desired minimum capacity
     *  参数 :minCapacity 这期望最小容量
     */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  //扩容1.5倍
    if (newCapacity - minCapacity < 0)  //如果新的容量小于期望的容量  那么直接newCapacity
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)  //扩容后的值大于这个MAX_ARRAY_SIZE,这个MAX_ARRAY_SIZE大小为Integer.MAX_VALUE - 8时候走下面语句
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • hugeCapacity(int minCapacity)方法,期望的值是小于0直接抛出超出内存溢出异常,否则返回三元表达式对应的结果值.
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
  • 小结:jdk7中的arrayList的对象创建类似于单例的饿汉式,而jdk8中的arrayList的对象创建类似于单例的懒汉式,延迟了数组的创建,节省内存.
LinkedList源码

LinkedList linkedList= 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;
    }
}
  • 调用add方法的时候,会调用linkLast方法
public boolean add(E e) {
    linkLast(e);
    return true;
}
  • linkLast方法(代码其实非常好理解,学好数据结构看源码就会轻松好多)
transient Node<E> first;  //指向第一个结点
transient Node<E> last;  //指向最后一个节点
void linkLast(E e) {
    final Node<E> l = last;   //先用个新结点保存最后的结点
    final Node<E> newNode = new Node<>(l, e, null); //然后建立个节点,让他的前一个节点为l(新创建的prev指向前面的元素,该元素可能为空),值为e,后一个节点为null
    last = newNode;
    if (l == null)  //原链表当前没有节点的时候
        first = newNode;  //那么新创建的结点就是第一个节点
    else  //原链表有结点
        l.next = newNode;  //那么让原链表和新创的节点连接起来,让l的next指向新链表,新链表的prev已经在创建的时候指过了
    size++;   
    modCount++;
}
Vector源码分析

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

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

  • 构造器
public Vector() {
    this(10);  //初始数组长度为10
}
  • 调用add操作
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • 扩容操作 (扩容为原来的二倍)
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);  //capacityIncrement空参构造时候为0
    //capacityIncrement有参构造指定的时候会按照这个值扩容                                 
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

List接口常用方法

Modifier and TypeMethod and Description
voidadd(int index, E element) 将指定的元素插入此列表中的指定位置(可选操作)。
booleanaddAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
Eget(int index) 返回此列表中指定位置的元素。
intindexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
intlastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
Eremove(int index) 删除该列表中指定位置的元素(可选操作)。
Eset(int index, E element) 用指定的元素(可选操作)替换此列表中指定位置的元素。
List<E>subList(int fromIndex, int toIndex) 返回此列表中指定的 fromIndex (含)和 toIndex之间的视图。
  • 方法测试1
@Test
public void test(){
    ArrayList<Object> list = new ArrayList<>();
    list.add(123);
    list.add("AA");
    list.add(new Person("Tom",18));
    System.out.println(list);
    //add(int index, E element)`将指定的元素插入此列表中的指定位置(可选操作)。
    list.add(1,"BB");
    System.out.println(list);
    List<Integer> list1 = Arrays.asList(1, 2, 3);
    //addAll(int index, Collection<? extends E> c)将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
    list.addAll(1,list1); //没索引时候默认为末尾添加
    System.out.println(list);
    //get(int index)返回此列表中指定位置的元素。
    int o = (int) list.get(1);
    System.out.println(o);

}
结果:
[123, AA, Person{name='Tom', age=18}]
[123, BB, AA, Person{name='Tom', age=18}]
[123, 1, 2, 3, BB, AA, Person{name='Tom', age=18}]
1
  • 测试2
@Test
public void test2(){
    ArrayList<Object> list = new ArrayList<>();
    list.add(123);
    list.add(456);
    list.add("AA");
    list.add(new Person("Tom",18));
    int i = list.indexOf(456);
    //int indexOf(Object obj):返回obj在集合中首次出现的位置,不存在返回-1
    int j = list.indexOf(3456); //找不到返回-1
    System.out.println("i = "+i+" j ="+j);
    //lastIndexOf(Object o)返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
    //remove(int index)删除该列表中指定位置的元素(可选操作),并返会删除的元素
    //没找到肯定是越界异常,因为指定的index没有
    Object obj = list.remove(0);
    System.out.println(obj);
    System.out.println(list);
    //set(int index, E element)用指定的元素(可选操作)替换此列表中指定位置的元素。
    list.set(1,"CC");
    System.out.println(list);
    //subList(int fromIndex, int toIndex)返回此列表中指定的fromIndex(含)和toIndex`之间的元素。
    List subList = list.subList(1, 3);
    System.out.println(subList);
    System.out.println(list);
}
结果:
i = 1 j =-1
123
[456, AA, Person{name='Tom', age=18}]
[456, CC, Person{name='Tom', age=18}]
[CC, Person{name='Tom', age=18}]
[456, CC, Person{name='Tom', age=18}]
  • 遍历List
@Test
public void test3(){
    ArrayList<Object> list = new ArrayList<>();
    list.add(123);
    list.add(456);
    list.add("AA");
    //方式一:Iterator
    Iterator<Object> iterator = list.iterator();
    while (iterator.hasNext()){
        Object next = iterator.next();
        System.out.println(next);
    }
    //方式二:foreach循环
    System.out.println("++++++++二++++++++");
    for (Object o:
         list) {
        System.out.println(o);
    }
    //方式三:普通的for循环
    System.out.println("++++++++三++++++++");
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}
结果:
123
456
AA
++++++++++++++++
123
456
AA
++++++++++++++++
123
456
AA
  • 总结:常用方法
  1. 增:add(Object obj)
  2. 删:remove(int index)
  3. 改:set(int index, E element)
  4. 查:get(int index)
  5. 插:add(int index,Object ele)
  6. 长度:size()
  7. 遍历:①Iterator迭代器的方式②增强for循环③普通的循环

面试小题

  • 结果是什么?
@Test
public void test(){
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    updateList(list);
    System.out.println(list);

}
private static void updateList(List list){
    Integer a=2;
    list.remove(a);   //也可以直接写成list.remove(new Integer(2))
}
答案:
[1,3]  
解释:a是个对象,通过remove移除的是对象为a的元素,所以是[1,3]
  • 改成这样结果又会是怎样?
@Test
public void test(){
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    updateList(list);
    System.out.println(list);

}
private static void updateList(List list){

    list.remove(2);
}
结果:
[1,2]
解释:移除的是下标为2位置的元素,因为他可以不装箱成类就找到,所以就不会去装箱,会把当做索引
  • 那么这个结果你应该也知道了吧?
@Test
public void test(){
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(8);
    updateList(list);
    System.out.println(list);

}
private static void updateList(List list){

    list.remove(8);
}
结果:IndexOutOfBoundsException
当然出错了,他找的是8下标,而不是对象,当数字能不自动装箱他肯定不自动装箱.
  • 这个结果你清楚么?
@Test
public void test(){
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(8);
    updateList(list);
    System.out.println(list);

}
private static void updateList(List list){

    boolean remove = list.remove("8");  //没有字符串8
    System.out.println(remove);  //false
}
结果:
false
[1, 2, 8]
  • 所以我们要正确区分list中的remove(int index)和remove(Object obj)

Collection子接口二:Set

|----Collection接口:单例集合,用来存储一个一个对象

​ |----Set接口:存储无序的、不可重复的数据 -->高中讲的"集合"

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

​ |----LinkedHashSet:作为hashSet的子类,遍历其内部数据时,可以按照添加的顺序去遍历.对于频繁的插入和遍历操作,LinkedHashSet效率高于HashSet

​ |----TreeSet:可以按照添加对象指定属性,进行排序.

  • new对象时候,底层实现还是对应的Map

HashSet

  • Set接口没有额外定义新的方法,使用的都是Collection中 声明过的方法.
    .

  • Set存储无序性、不可重复的数据

    一以HashSet为列说明

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

    • 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:岁哦因为值),判断此位置上是否已经有元素,

      • 如果此位置上没有元素,则元素a添加成功. —>情况1
      • 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值
        • 如果hash值不相同,则元素a添加成功, —>情况2
        • 如果hash值相同,进而需要调用元素a所在类的equals()方法,equals返回true,元素a添加失败,返回false,则元素a添加成功 —>情况3
    • 对于添加成功的情况2和情况3而言,元素a与已经存在指定索引位置上的数据以链表形式存储.

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

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

      总结:7上8下

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

  1. 要求:向HashSet中添加的数据,其所在的类一定要重写hashCode()和equals()方法
  2. 要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具备相等的散列码.
  3. 重写的两个小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode()值. 尽量用idea自动生成的一般就够用了

LinkedHashSet

//LinkedHashSet的使用
//LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
//优点:对于频繁的插入和遍历操作,LinkedHashSet效率高于HashSet
@Test
public void test(){
    Set set = new LinkedHashSet();
    set.add(456);
    set.add("AA");
    set.add("CC");
    set.add("oo");
    Iterator iterator = set.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}
  • 结果是和添加顺序一样的,但这不叫做有序,只是LInkedHashSet里面有引用存上一个和下一个.
456
AA
CC
oo

TreetSet(采用红黑树存储结构)

  1. 向TreetSet中添加的数据,要求是相同类的对象
  2. 两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
  3. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0、不再是equals方法了
  4. 定制排序中,比较两个对象是否相同的标准为:comparte()返回0,不再是equals方法了
  • 测试 User类
public class User implements Comparable{
    private String name;
    private int age;

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


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

    //按照姓名从小到大排序,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User user=(User)o;
            //return this.name.compareTo(user.name);
            //可以进行二级排序
            int compare=this.name.compareTo(user.name);
            if(compare!=0){
                return compare;
            }else{
                return Integer.compare(this.age,user.age);
            }

        }else{
            throw new RuntimeException("输入的类型不匹配");
        }

    }
}
  • 测试代码
 @Test
    public void test(){
        Set set = new TreeSet();
        //失败:不能添加不同类的对象
//        set.add(456);
//        set.add("AA");

        //举例一:
//        set.add(123);
//        set.add(456);
//        set.add(43);
//        set.add(11);
//        set.add(8);
//        System.out.println(set); //[8, 11, 43, 123, 456]

        //举例二:
//        set.add("b");
//        set.add("A");
//        set.add("B");
//        set.add("aD");
//        set.add("aC");  //按照字典序进行拍的,先比较的是第一个字母
//        System.out.println(set);  //[A, B, aC, aDbb, b]

        //举例三   User不去实现Comparable时候会报错,
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56)); //如果没有自定义年龄排序,那么这条数据将会失去
        System.out.println(set); //[User{name='Jack', age=33}, User{name='Jack', age=56}, User{name='Jerry', age=32}, User{name='Jim', age=2}, User{name='Tom', age=12}]



    }
  • 定制排序
   @Test
    public void test2(){
        Comparator com = new Comparator() {
            //按照年龄从小到大排列
            @Override
            public 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());//user方法去增加下get set方法
                }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("Jack",33));
        set.add(new User("adc",33));
        set.add(new User("Jack",56));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
结果:
User{name='Jim', age=2}
User{name='Tom', age=12}
User{name='Jerry', age=32}
User{name='Jack', age=33}
User{name='Jack', age=56}

面试题

  • Person类重写有hashCode和equals方法
public class Person {
    private int id;
    private String name;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

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

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

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

  • Test
public class Test {
    public static void main(String[] args) {
        HashSet<Object> set = new HashSet<>();
        Person p1 = new Person(1001, "AA");
        Person p2 = new Person(1002, "BB");

        set.add(p1);
        set.add(p2);

        p1.setName("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);
    }
}

结果可能会有一点出人意料,但是也在情理之中

[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
[Person{id=1002, name='BB'}, Person{id=1001, name='CC'},Person{id=1001, name='CC'},  Person{id=1001,name='AA'}]
  • 解释
  1. set添加元素的时候按照的是hashCode和equals方法添加的,前两个元素添加好后,把p1 name改了,在调用移除元素的方法的时候,底层是根据改后的name和原来的id计算hashCode值去找对应位置是否有元素,没有就不删除,有元素那就看equals方法调用是否相同,相同了删除,不相同了不删除.
  2. 那么第二个输出语句就不难理解了,两者的hashCode值不一样,都会存在set集合中
  3. 第三个,根据添加的元素算出来的hashCode值的时候对应位置有元素,然后通过equals方法判断元素是否相同,相同了就不需要再重复加了,不同那么就会加进去,显然第三个和改过name后的p1是不相同的,所以照样会添加成功.

Map接口

在这里插入图片描述

一:Map的实现类的结构情况

|----Map:双列数据 存储key-value对的数据 ----类似于高中的函数:y=f(x)

​ |----HashMap:作为Map的主要实现类:线程不安全的,效率高;可以存储null的key和value

​ |----LinkedHashMap:保证在遍历map元素的时候,可以按照添加顺序实现遍历.

​ 原因:在原有的HashMap底层的基础上,添加了一对指针,指向前一个和后一个元素

​ 对于频繁的遍历操作,此类执行效率高于HashMap

​ |----TreeMap:可以按照添加的key-value对进行排序,实现排序遍历.此时考虑key的自然排序和定制排序

​ 底层使用红黑树

​ |----Hashtable:作为古老的实现类:线程安全的,效率低,不能存储null的key和value

​ |----Properties:常用类处理配置文件key和value都是String类型

HashMap的底层:数组+链表 (jdk7及之前)

​ 数组+链表+红黑树(jdk8)

  • 测试key value是否可以存null
    @Test
    public void test(){
        HashMap<Object, Object> map = new HashMap<>();
        Hashtable<Object, Object> hashtable = new Hashtable<>();
        map.put(null,null);
        map.put(null,1);
//        hashtable.put(null,1);//报异常
//        hashtable.put(1,null);//报异常

    }

Map结构的理解

Map中的key:无序的、 不可重复的,使用set存储所有的key —>key所在的类要重写equals()方法和hashCode()方法 (以hashMap为例)

Map中的value:无序的、可重复的,使用Collection存储所有的value–>value所在的类要重写equals()根据规定一般也要写hashCode

一个键值对:key-value构成了一个Entry对象

Map中的entry:无序的、不可重复的,使用Set存储所有的entry

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()方法,比较:
    
      ​				如果equals()返回false: 此时key1-value1添加成功   --->情况3
    
      ​				如果equals()返回true:使用value1替换相同key的value值
    
    补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储
    
    在不断的添加过程中:会涉及扩容问题,当超出临界值(且要存放的位置非空时),扩容,默认的扩容方式:扩容为原来容量的2,并将原有的数据复制过来
    

jdk8 相较于jdk7在底层实现方面的不同

	1. new hashMap():在底层没有创建一个长度为16Entry数组
    2. jdk 8底层的数组是: Node[],而非Entry[]
    3. 首次调用put方法的时候,底层创建长度为16的数组
    4.jdk7底层结构只有:数组+链表.jdk8中底层结构:数组+链表+红黑树
        当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度>64,
		此时此索引位置上的所有数据改为使用红黑树存储.
  • 重要常量:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16   HashMap的默认容量16
static final int MAXIMUM_CAPACITY = 1 << 30;//HashMap最大支持容量 2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//HashMap的默认加载因子
int threshold;  // 扩容的临界值,=容量*填充因子,首次扩容时候是0.75*16=12
static final int TREEIFY_THRESHOLD = 8;//Bucket中链表长度大于该默认值,转为红黑树
static final int MIN_TREEIFY_CAPACITY = 64; //桶中的Node被树化时最小的hash表容量,当桶中Node的数量达到需要变成红黑树的时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应该执行resize扩容操作 这个MIN_TREEIFY_CAPACITY值至少是TREEIFY_THRESHOLD的4倍
jdk7的hashMap
jdk8的hashMap
  • 构造器
public HashMap() {
	//加载因子赋值为0.75
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
  • put方法
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
  • putVal方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    //声明tab为Node类型
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)//如果首次调用,那么||前面就是true,就会进入下面语句进行扩容
        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 1st
                        treeifyBin(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 key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
  • resize()方法
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table; 第一次时时null
    int oldCap = (oldTab == null) ? 0 : oldTab.length;   //oldCap=0
    int oldThr = threshold;  //oldThr=0
    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 threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;   //把默认16赋值给newCap
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //16*0.75=12
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;  //12
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];  //造数组
    table = newTab; //造好的数组给table
    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 order
                    Node<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;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.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;
}
  • treeifyBin()
final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

LInkedHashMap底层实现原理(了解)

public class LinkedHashMap<K,V>
    extends HashMap<K,V>

调用put方法时候用的还是hashMap的,然后putValue也是hashMap中的,但是他把putVal中用到的newNode(参数…)这个方法重写了

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    LinkedHashMap.Entry<K,V> p =
        new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    linkNodeLast(p);
    return p;
}

hashMap中的内部类:Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }

    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

LinkedHashMap中的内部类:Entry

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);
    }
}

Map中定义的方法

Modifier and TypeMethod and Description
voidclear() 从该地图中删除所有的映射(可选操作)。
default Vcompute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 尝试计算指定键的映射及其当前映射的值(如果没有当前映射, null )。
default VcomputeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 如果指定的键尚未与值相关联(或映射到 null ),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非 null
default VcomputeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 如果指定的密钥的值存在且非空,则尝试计算给定密钥及其当前映射值的新映射。
booleancontainsKey(Object key) 如果此映射包含指定键的映射,则返回 true
booleancontainsValue(Object value) 如果此地图将一个或多个键映射到指定的值,则返回 true
Set<Map.Entry<K,V>>entrySet() 返回此地图中包含的映射的Set视图。
booleanequals(Object o) 将指定的对象与此映射进行比较以获得相等性。
default voidforEach(BiConsumer<? super K,? super V> action) 对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。
Vget(Object key) 返回到指定键所映射的值,或 null如果此映射包含该键的映射。
default VgetOrDefault(Object key, V defaultValue) 返回到指定键所映射的值,或 defaultValue如果此映射包含该键的映射。
inthashCode() 返回此地图的哈希码值。
booleanisEmpty() 如果此地图不包含键值映射,则返回 true
Set<K>keySet() 返回此地图中包含的键的Set视图。
default Vmerge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非空值相关联。
Vput(K key, V value) 将指定的值与该映射中的指定键相关联(可选操作)。
voidputAll(Map<? extends K,? extends V> m) 将指定地图的所有映射复制到此映射(可选操作)。
default VputIfAbsent(K key, V value) 如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。
Vremove(Object key) 如果存在(从可选的操作),从该地图中删除一个键的映射。
default booleanremove(Object key, Object value) 仅当指定的密钥当前映射到指定的值时删除该条目。
default Vreplace(K key, V value) 只有当目标映射到某个值时,才能替换指定键的条目。
default booleanreplace(K key, V oldValue, V newValue) 仅当当前映射到指定的值时,才能替换指定键的条目。
default voidreplaceAll(BiFunction<? super K,? super V,? extends V> function) 将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。
intsize() 返回此地图中键值映射的数量。
Collection<V>values() 返回此地图中包含的值的Collection视图。
  • 添加删除修改操作
   @Test
    public void test(){
        HashMap<Object, Object> map = new HashMap<>();
        //添加
        map.put("AA",123);
        map.put(45,123);
        //修改
        map.put("AA",321);
        System.out.println(map);

        HashMap<Object, Object> map1 = new HashMap<>();
        map1.put("CC",123);
        map1.put("DD",123);
        map.putAll(map1);
        System.out.println(map);

        //remove(Object key)
        Object value = map.remove("CC"); //没有返回就是null
        System.out.println(value);  //CC对应的value
        System.out.println(map);
        //clear()
        map.clear(); //map=null操作不同
        System.out.println(map.size());
        System.out.println(map);

    }

结果:

{AA=321, 45=123}
{AA=321, CC=123, DD=123, 45=123}
123
{AA=321, DD=123, 45=123}
0
{}
  • 判空 查key value是否存在
@Test
public void test2(){
    HashMap<Object, Object> map = new HashMap<>();
    map.put("AA",123);
    map.put(45,123);
    map.put("AA",321);
    //Object get(Object key)
    System.out.println(map.get(45));  //123
    //`boolean containsKey(Object key)`
    boolean isExist = map.containsKey("88");
    System.out.println(isExist); //false
    // containsValue(Object value)
    System.out.println(map.containsValue(123)); //true
    map.clear();
    System.out.println(map.isEmpty());  //true
}
  • 遍历
   @Test
    public void test3(){
        HashMap map = new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("AA",321);
        System.out.println(map);
        System.out.println("====");

        //遍历所有的key集:keySet
        Set set=map.keySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        System.out.println("====");
        //遍历所有的value
        Collection values = map.values();
        for (Object obj : values) {
            System.out.println(obj);
        }

        System.out.println("====");
        //遍历所有的key-value
        //方式一:entrySet
        Set entrySet = map.entrySet();
        System.out.println(entrySet);
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()) {
            //entrySet集合中的元素都是entry
            Map.Entry entry= (Map.Entry) iterator1.next();
            System.out.println(entry.getKey()+"--->"+entry.getValue());
//            System.out.println(iterator1.next());
        }
        System.out.println("===方式二===");
        //方式二:
        Set keySet = map.keySet();
        Iterator iterator2 = keySet.iterator();
        while (iterator2.hasNext()) {
            Object key = iterator2.next();
            Object value = map.get(key);
            System.out.println(key+"--->"+value);
        }

    }

TreeMap

向TreeMap中添加key-value,要求key必须是同一个类创建的对象

因为要按照key进行排序:自然排序、定制排序

  • User类
package SetBao.ti;

public class User implements Comparable{
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = 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 "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //按照姓名从小到大排序,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User user=(User)o;
            //return this.name.compareTo(user.name);
            //可以进行二级排序
            int compare=this.name.compareTo(user.name);
            if(compare!=0){
                return compare;
            }else{
                return Integer.compare(this.age,user.age);
            }

        }else{
            throw new RuntimeException("输入的类型不匹配");
        }

    }
}
  • 自然排序
 //自然排序
    @Test
    public void test4(){
        TreeMap<Object, Object> map = new TreeMap<>();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);
        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()) {
            //entrySet集合中的元素都是entry
            Map.Entry entry= (Map.Entry) iterator1.next();
            System.out.println(entry.getKey()+"--->"+entry.getValue());
//            System.out.println(iterator1.next());
        }
    }

结果:

User{name='Jack', age=20}--->76
User{name='Jerry', age=32}--->89
User{name='Rose', age=18}--->100
User{name='Tom', age=23}--->98
  • 定制排序
 //定制排序
    @Test
    public void test5(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public 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());
                }
                throw new RuntimeException("输入的类型不匹配");

            }
        });
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",32);
        User u3 = new User("Jack",20);
        User u4 = new User("Rose",18);
        map.put(u1,98);
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()) {
            //entrySet集合中的元素都是entry
            Map.Entry entry= (Map.Entry) iterator1.next();
            System.out.println(entry.getKey()+"--->"+entry.getValue());
//            System.out.println(iterator1.next());
        }
    }

结果:

User{name='Rose', age=18}--->100
User{name='Jack', age=20}--->76
User{name='Tom', age=23}--->98
User{name='Jerry', age=32}--->89

Hashtable子类:Properties

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        Properties pros= new Properties();
        FileInputStream fis = new FileInputStream("jdbc.properties");

        pros.load(fis); //加载流对应的文件
        String name = pros.getProperty("name");
        System.out.println(name);

        String password1 = pros.getProperty("password");
        System.out.println(password1);

        Object password = pros.get("password");
        System.out.println(password);
    }
}

结果:

Tom
123456
123456

面试题

  1. HashMap的底层实现原理?
  2. HashMap 和Hashtable的异同?
  3. CurrentHashMap(有分段锁的技术)与HashTable的异同?(暂时不讲)
  4. 你对put/get方法的认识?扩容机制,负载因子,吞吐临界值?

Collections工具类

  • Collections:

    操作Collection、Map的工具类

  • 面试题:Collection 和Collections的区别?

    一个是接口,一个是类,Collections是操作Collection的工具类

  1. reverse(List):反转List中元素的顺序
  2. shuffle(Life):对List集合元素进行随机排序
  3. sort(List):根据元素的自然顺序对指定List集合元素按升序排列
  4. sort(List,Comparator):根据指定的Compartor产生的顺序对List集合进行排序
  5. swarp(List,int ,int):将指定List集合中的i处元素和j处元素进行交换
   @Test
    public void test(){
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(76);
        list.add(76);
        list.add(-92);
        System.out.println(list);
//

        //随机排序
//        Collections.shuffle(list);
//        System.out.println(list);
        Collections.swap(list,1,2);
        System.out.println(list);
        int frequency = Collections.frequency(list, 76);
        System.out.println(frequency); //2  某个数出现的频率
    }
  • 复制操作
 @Test
    public 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());
        Collections.copy(dest,list);
        System.out.println(dest);
    }
  • Collections 类汇总提供了多个 synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时线程安全问题,也可以操作Map
@Test
public void test3(){
    List list = new ArrayList();
    list.add(123);
    list.add(43);
    list.add(765);
    list.add(-97);
    list.add(0);

    //返回的list1即为线程安全的List
    List list1 = Collections.synchronizedList(list);

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值