【java学习之路】(java SE篇)008.集合

本文详细介绍了Java集合框架的各种组件,包括Collection、List、Set、Map等接口及其典型实现类的特性与应用场景,同时还深入探讨了泛型、迭代器、常用工具类的使用方法。

集合

什么情况使用集合框架?

​ 如果不知道程序运行时需要多少对象,或者需要更复杂的方式存储对象

比较重要的

学习路线

Collection类

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

/*
    集合框架:
        Collection:存放的是单一值
            特点:
                1.可以存放不同类型的数据,而数组只能存放同一类型的数据
                2.当使用arrayList子类实现的时候,初始长度是10,当长度不够的时候,会进行自动扩容
            api方法(增,删,查):
                增加数据的方法
                    add:要求必须传入的参数是Object对象,因此当写入基本是基本类型的时候,进行了装箱和拆箱,只不过过程不可见
                    addAll:添加另一个集合的元素到此集合中

                删除数据的方法
                    clear:只是清空集合中的元素,但是此集合对象并没有被回收
                    remove:删除指定元素
                    removeAll:删除指定集合
                    retainAll:用于保留 arraylist 中在指定集合中也存在的那些元素,也就是删除指定集合中不存在的那些元素
                        如果 arraylist 中删除了元素则返回 true。
                        如果 arraylist 类中存在的元素与指定 collection 的类中元素不兼容,则抛出 ClassCastException 异常。
                        如果 arraylist 包含 null 元素,并且指定 collection 不允许 null 元素,则抛出 NullPointerException 。

                查询数据的方法
                    contains:判断集合中是否包含指定的元素值
                    containsAll:判断集合中是否包含另一个集合
                    isEmpty:判断集合是否为空
                    size:返回当前集合的大小

                集合转数组的操作
                    toArray:将集合转化成数组
 */
public class CollectionDemo {
    public static void main(String[] args) {
        //Constructs an empty list with an initial capacity of ten.
        Collection collection = new ArrayList();//必须子类实现,接口无法new
        collection.add(1);
        collection.add(true);
        collection.add(1.23);
        collection.add("abc");
        System.out.println(collection);

        ((ArrayList)collection).add(0,"java");//强制类型转换
        System.out.println(collection);

        Collection collection1 = new ArrayList();
        collection1.add("a");
        collection1.add("b");
        collection1.add("c");
        collection1.add("d");
        collection.addAll(collection1);//将另一个集合全部元素添加进来
        System.out.println(collection);

//        collection.clear();
//        System.out.println(collection);

        System.out.println(collection.contains("a"));
        System.out.println(collection.containsAll(collection1));

        System.out.println(collection.isEmpty());

//        collection.remove("a");
//        System.out.println(collection);

        System.out.println(collection1.retainAll(collection));//false
//        System.out.println(collection.retainAll(collection1));
                // true 并且collection中只剩下[a, b, c, d],即collection1中所包含的元素

        Object[] objects = collection.toArray();
        //注意:如果不确定返回的类型,可以使用.var进行补全    collection.toArray().var

        collection.add("a");
        System.out.println(collection);
        //[java, 1, true, 1.23, abc, a, b, c, d, a]  不唯一

    }
}

List和Set接口

Collection接口中存储一组不唯一无序的对象

List接口中存储一组不唯一有序的对象

Set接口中存储一组唯一无序的对象

Map接口中存储一组键值对象,提供keyvalue的映射

1.List接口的实现类

​ List特点:有序,不唯一

ArrayList 实现了长度可变的数组,在内存中分配连续空间

​ 优点:遍历元素和随机访问元素的效率比较高

​ 缺点:添加和删除需要大量移动元素效率低,按内容查询效率低

import java.util.ArrayList;
import java.util.List;

/*
    集合框架:
        List
            特点:
                1.可以存放不同类型的数据,而数组只能存放同一类型的数据
                2.当使用arrayList子类实现的时候,初始长度是10,当长度不够的时候,会进行自动扩容
            api方法(增,删,查):
            
                继承Collection

                get:输出指定下标的元素
                indexOf:输出指定元素的下标(从前往后找)
                lastIndexOf:输出指定元素的下标(从后往前找)
                set:将此列表中指定位置的元素替换为指定元素
                subList:截取指定下标元素到新的集合中

                jdk9中新增了一个of方法:可以给集合一次性添加多个元素
                List of = List.of(1,2,3,4);
                    结果为:[1,2,3,4]
                    使用前提:当集合中存储的元素个数已经确定了,不再改变时使用。
                        注意:
                            1.of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类
                            2.of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
                            3.Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常

 */
public class CollectionDemo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("a");
        list.add(1);
        list.add(true);
        list.add("a");
        System.out.println(list);
        System.out.println(list.get(0));//输出指定下标的元素
        System.out.println(list.indexOf("a"));//输出指定元素的下标(从前往后找)
        System.out.println(list.lastIndexOf("a"));//输出指定元素的下标(从后往前找)
        list.set(0, "java");//将此列表中指定位置的元素替换为指定元素
        System.out.println(list);//[a, 1, true, a]->[java, 1, true, a]
        List list1 = list.subList(0, 2);//截取指定下标元素到新的集合中
        System.out.println(list1);//[java, 1]
    }
}

LinkedList采用链表存储方式

​ 优点:插入、删除元素效率比较高

​ 缺点:遍历和随机访问元素效率低下

import java.util.LinkedList;
/*
    LinkedList拥有更加丰富的方法实现,需要使用的时候查询API即可,不需要全部记忆
 */
public class LinkedListDemo {

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(123);
        linkedList.add(false);
        linkedList.add("abc");
        System.out.println(linkedList);

        linkedList.add(0, "java");
        System.out.println(linkedList);

        //LinkedList特有的头插和尾插
        linkedList.addFirst("first");
        linkedList.addLast("last");
        System.out.println(linkedList);

        System.out.println(linkedList.element());//获取对应的第一个元素
        linkedList.offer(33);//添加一个元素
        System.out.println(linkedList);
    }
}

2.Vector和ArrayList

**面试经常问区别:
** 1.ArrayList是线程不安全的,效率高;Vector是线程安全的,效率低
2.ArrayList在进行扩容的时候,扩容1.5倍;Vector在扩容的时候,扩容2倍

import java.util.Vector;
/*
    1.Vector也是List接口的一个子类实现
    2.Vector跟ArrayList一样,底层都是用数组进行实现的
    面试经常问区别:
        1.ArrayList是线程不安全的,效率高;Vector是线程安全的,效率低
        2.ArrayList在进行扩容的时候,扩容1.5倍;Vector在扩容的时候,扩容2倍
 */
public class VectorDemo {

    public static void main(String[] args) {
        Vector vector = new Vector();
        vector.add(1);
        vector.add(true);
        vector.add("123");
        System.out.println(vector);
    }
}

练习

import java.util.Objects;

public class Dog {

    private String name;
    private String color;

    public Dog(){

    }

    public Dog(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

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

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

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}
import java.util.ArrayList;
import java.util.List;

public class ListTest {

    public static void main(String[] args) {
        List list = new ArrayList();
        Dog d1 = new Dog("大黄","red");
        Dog d2 = new Dog("二黄","black");
        Dog d3 = new Dog("三黄","green");

        list.add(d1);
        list.add(d2);
        list.add(d3);

        System.out.println(list);
        //[Dog{name='大黄', color='red'}, Dog{name='二黄', color='black'}, Dog{name='三黄', color='green'}]
        System.out.println(list.size());
        list.remove(d1);
        System.out.println(list);
        System.out.println(list.contains(d2));

        //扩展
        Dog d4 = new Dog("二黄","black");
        //此时d4返回什么?
//        System.out.println(list.contains(d4));//false  地址不一样
        //怎么把结果变成true----->重写equals和hasCode,将对比地址改为对比实际属性值
        System.out.println(list.contains(d4));//true


    }
}

Iterator接口

Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayListHashSet等集合。

Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。

迭代器 it 的基本操作是 next 、hasNext 和 remove

​ 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

​ 调用 it.hasNext() 用于检测集合中是否还有元素。

​ 调用 it.remove() 将迭代器返回的元素删除。

获取一个迭代器

集合想获取一个迭代器可以使用 iterator() 方法:

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;

public class RunoobTest {
    public static void main(String[] args) {

        // 创建集合
        ArrayList sites = new ArrayList();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");

        // 获取迭代器
        Iterator it = sites.iterator();

        // 输出集合中的第一个元素
        System.out.println(it.next());//Google
    }
}

循环集合元素

让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环

while(it.hasNext()) {
    System.out.println(it.next());
}

删除元素

要删除集合中的元素可以使用 remove() 方法。

以下实例我们删除集合中小于 10 的元素:

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;

public class RunoobTest {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        numbers.add(12);
        numbers.add(8);
        numbers.add(2);
        numbers.add(23);
        Iterator<Integer> it = numbers.iterator();
        while(it.hasNext()) {
            Integer i = it.next();
            if(i < 10) {  
                it.remove();  // 删除小于 10 的元素
            }
        }
        System.out.println(numbers);//[12, 23]
    }
}

关于解决使用remove出现的并发操作异常的问题

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

/*
    在java代码中包含三种循环的方式
        do...while、for、while
        还有一种增强for循环的方式,可以简化循环的编写

    在所有的集合类,都默认实现了Iterable的接口,实现此接口意味着具备了实现增强for循环的能力
        增强for循环本质上是用的也是interator迭代器的功能
        方法:
            iterator()
            forEach()

    在iterator()方法中要求返回一个Iterator的接口子类实例化对象
        此接口中包含了:
            hasNext()
            next()

    实现了Iterable的接口,当调用它的时候返回一个迭代器iterator(),迭代器iterator()也是一个接口对象,实现出来后用来调用hasNext()和next()

    在使用iterator进行迭代的过程中,如果删除其中某个元素会报错,并发操作异常,因此如果遍历的同时需要修改元素,建议使用listIterator()
        ListIterator迭代器提供了向前和向后两种迭代方式,始终是通过cursor和lastret的指针来获取元素值及向下的遍历索引
        当使用向前遍历的时候,必须要保证指针在迭代器的结尾,否则无法获取结果值
 */
public class IteratorDemo {

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
//        for (int i=0;i<list.size();i++){
//            System.out.println(list.get(i));
//        }

        //迭代器
//        Iterator iterator = list.iterator();
        //并发操作异常
//        while (iterator.hasNext()){
//            Object o = iterator.next();
//            if(o.equals(1)){
//                list.remove(1);
//            }
//            System.out.println(o);
        ListIterator iterator = list.listIterator();
        while (iterator.hasNext()){
            Object o = iterator.next();
            if(o.equals(1)){
                iterator.remove();
            }
            System.out.println(o);
        }

        System.out.println("-------------");
        while (iterator.hasPrevious()){
            System.out.println(iterator.previous());
        }

        //简便写法
        System.out.println("------------------");
        for (Object i : list){
            System.out.println(i);
        }
    }
}

Set接口

HashSet采用HashTable哈希表存储结构

​ 优点:添加速度快,查询速度快,删除速度快

​ 缺点:无序

LinkedHashSet

​ 采用哈希表存储结构,同时使用链表维护次序

​ 特点:有序

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/*
    1.set中存放的是无序,唯一的数据
    2.set不可以通过下标获取对应的元素的值,因为它是无序的
 */
public class SetDemo {

    public static void main(String[] args) {
        Set set = new HashSet();
        set.add(123);
        set.add("abc");
        set.add(true);
        System.out.println(set);//[abc, 123, true]  无序

        set.add(123);
        System.out.println(set);//[abc, 123, true]  唯一

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("--------------------------");
        //将while循环改成for循环
        for(Iterator iter = set.iterator();iter.hasNext();){
            System.out.println(iter.next());
        }//推荐使用此种方式
        
        
    }
}

TreeSet采用二叉树(红黑树)的存储结构

​ 优点:有序(排序后升序),查询速度比List快

​ 缺点:查询速度没有HashSet

红黑树简介

是一种自平衡二叉树,相比于平衡二叉树(AVL树),红黑树牺牲了部分平衡性以换取插入、删除时少量的旋转操作,整体来说性能要优于AVL树

特点:

​ 1.节点是红色或者黑色

​ 2.根节点是黑色

​ 3.每个叶节点(NIL节点)是黑色

​ 4.从任意节点到其每个叶子的所有路径都包含相同数目的黑色节点

​ 5.每个红色节点的两个子节点都为黑色(从每个叶子节点到根的路径上不能有两个连续的红色节点)

​ 6.最长路径不超过最短路径的两倍

例如:

TreeSet和HashSet

import java.util.Objects;

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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

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

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

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/*
    1.set中存放的是无序,唯一的数据
    2.set不可以通过下标获取对应的元素的值,因为它是无序的
    3.设置元素的时候,如果是自定义对象,会查找对象中的equals和hashcode的方法,如果没有,比较的是地址
 */
public class SetDemo {

    public static void main(String[] args) {
//        TreeSet treeSet = new TreeSet();//存储数据结构:红黑树
//        treeSet.add(123);
//        treeSet.add(2);
//        treeSet.add(7);//必须是相同的数据类型
//        System.out.println(treeSet.ceiling(3));//返回此集合中最小元素大于或等于给定元素,如果没有此元素,则返回 null 。
//        System.out.println(treeSet);
        
        HashSet hashSet = new HashSet();//用hasMap实现
        hashSet.add(new Person("张三",12));
        hashSet.add(new Person("张三",12));
        hashSet.add(new Person("李四",13));
        hashSet.add(new Person("王五",14));
        hashSet.add(new Person("赵六",15));
        System.out.println(hashSet);
    }
}

自建类如何让hashSet判断输入内容是否相同

import java.util.Objects;

/*
    为了提高比较的效率,会根据内容算出一个hash值,用hash值来判断是否相同,效率会提升
    但hash值是不安全的,hash值一致但内容不一定相同
    如果出现hash值相同的情况,那么就必须使用equals()方法来判断内容是否相同

    hashCode()添加内容时先用key.hasCode(),计算hash值作比较,如果hash值相等
    equals()在调用key.equals(k)比较内容是否相等
    这样即保证了效率又保证了安全性
 */
public class Students {

    private  int no;
    private String name;

    public Students(int no, String name) {
        this.no = no;
        this.name = name;
    }

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

    //重写
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Students students = (Students) o;
        return no == students.no &&
                Objects.equals(name, students.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(no, name);
    }
}
import java.util.HashSet;

public class HashSetDemo2 {

    public static void main(String[] args) {

        Students s1 = new Students(101, "jim1");
        Students s2 = new Students(102, "jim2");
        Students s3 = new Students(103, "jim3");
        Students s4 = new Students(101, "jim1");
        Students s5 = new Students(105, "jim5");

        HashSet<Students> set = new HashSet<>();
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        set.add(s5);

        System.out.println(set);//输出5个
        //面试重点:
        //[Students{no=103, name='jim3'}, Students{no=105, name='jim5'}, Students{no=102, name='jim2'}, Students{no=101, name='jim1'}, Students{no=101, name='jim1'}]
        
        //为什么?

        //因为Students并没有默认的equals()和hasCode()方法,因此去调用Object类中的
            //Object中的hasCode()
                //public native int hasCode():值是对象地址,对象地址是相同的
            //Object中的equals()
                //默认比较对象地址

        //解决方法:在自己的类中重写
        //输出4个
        //[Students{no=101, name='jim1'}, Students{no=102, name='jim2'}, Students{no=103, name='jim3'}, Students{no=105, name='jim5'}]

    }
}

Comparable接口

import java.util.Objects;

public class Person implements Comparable{

    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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

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

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

    /**
     * 内部比较器
     * 此比较器按照age的长度进行比较
     * @param o
     * @return
     */
    @Override
    public int compareTo(Object o) {
        Person p = (Person)o;
        if(p.age>this.age){
            return 1;
        }else if(p.age<this.age){
            return -1;
        }else return 0;
    }
}
import java.util.*;

/*
    树中的元素是要默认进行排序操作的,如果是基本类型的话,自动比较,如果是引用类型的话,需要自己自定义比较器
        比较器分类:
            内部比较器:定义在元素的类中,通过实现comparable接口来实现
            外部比较器:定义在当前类中,通过Comparator接口来实现,但是要将该比较器传到集合中
                外部比较器可以定义成一个工具类,此时所有需要比较的规则如果一致的话,可以复用,而内部比较器只有在存储当前对象的时候才可以使用
                如果两者同时存在,使用外部比较器
 */
public class SetDemo implements Comparator<Person> {//外部比较器

    public static void main(String[] args) {
        //存储数据结构:红黑树
        TreeSet treeSet = new TreeSet(new SetDemo());//new SetDemo() 将外部比较器传入
//        treeSet.add(123);
//        treeSet.add(2);
//        treeSet.add(7);//必须是相同的数据类型
//        System.out.println(treeSet.ceiling(3));//返回此集合中最小元素大于或等于给定元素,如果没有此元素,则返回 null 。
//        System.out.println(treeSet);
        treeSet.add(new Person("张三",12));
        treeSet.add(new Person("张三",12));
        treeSet.add(new Person("李四",13));
        treeSet.add(new Person("王五",14));
        treeSet.add(new Person("赵六",15));
        System.out.println(treeSet);//运行报错,原因是运行的时候无法排序
        //解决方法,实现一个比较器Comparable(内部比较器)
        //[Person{name='赵六', age=15}, Person{name='王五', age=14}, Person{name='李四', age=13}, Person{name='张三', age=12}]

//        HashSet hashSet = new HashSet();
//        hashSet.add(new Person("张三",12));
//        hashSet.add(new Person("张三",12));
//        hashSet.add(new Person("李四",13));
//        hashSet.add(new Person("王五",14));
//        hashSet.add(new Person("赵六",15));
//        System.out.println(hashSet);
    }

    @Override
    public int compare(Person o1, Person o2) {
        if(o1.getAge()>o2.getAge()){
            return 1;
        }else if(o1.getAge()<o2.getAge()){
            return -1;
        }else return 0;

    }
}

泛型

基本概念

import java.util.ArrayList;
import java.util.List;
/*
    当做一些集合的统一操作的时候,需要保证集合的类型是统一的,此时需要泛型来进行控制
        优点:
            1.数据安全
            2.获取数据时效率较高
        因此给集合中的元素设置相同的类型就是泛型的基本需求
        使用:
            在定义对象的时候,通过<>中设置合理的类型来进行实现
 */
public class FanXingDemo {

    public static void main(String[] args) {
//        List list = new ArrayList();
//        list.add(1);//new Integer
//        list.add("abc");//new String("abc")
//        list.add(true);//new Boolean(true)
//        list.add(new Person("zhangsan",15));
//        System.out.println(list);

        List<String> list = new ArrayList<String>();
        list.add("1");//new Integer
        list.add("abc");//new String("abc")
        list.add("true");//new Boolean(true)
        list.add(new Person("zhangsan",15).toString());

        //普通for循环
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }

        System.out.println("---------------------");

        //增强for循环
//        for(Object iter : list){
//            System.out.println(iter);
//        }
        for(String iter : list){
            System.out.println(iter);
        }
    }
}

泛型的高阶运用

1.泛型类
public class FanXingClass<A> {//A仅仅起到占位作用

    private int id;
    private A a;

    public int getId() {
        return id;
    }

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

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    public void show(){
        System.out.println("id:"+id+" ,A:"+a);
    }
}
import java.util.ArrayList;
import java.util.List;
/*
    泛型的高阶运用
        1.泛型类
        	在定义类的时候在类名后面添加<E,K,V......>,起到占位的作用,方法类中的方法的返回值和属性的类型都可以使用
 */
public class FanXingDemo {

    public static void main(String[] args) {
        //1.泛型类
        FanXingClass<String> fxc = new FanXingClass<String>();
        fxc.setA("java");
        fxc.setId(1);
        fxc.show();

        FanXingClass<Integer> fxc2 = new FanXingClass<Integer>();
        fxc2.setA(34);
        fxc2.setId(2);
        fxc2.show();

        FanXingClass<Person> fxc3 = new FanXingClass<Person>();
        fxc3.setA(new Person("AAA",123));
        fxc3.setId(3);
        fxc3.show();
    }
}
2.泛型接口

第一种实现方式

public interface FanXingInterface<B> {

    public B test();

    public void test2(B b);
}
public class FanXingInterfaceSub<B> implements FanXingInterface<B>{//仍不指定子类类型

    @Override
    public B test() {
        return null;
    }

    @Override
    public void test2(B b) {
        System.out.println(b);
    }
}
import java.util.ArrayList;
import java.util.List;
/*
    泛型的高阶运用
        2.泛型接口
            在定义接口的时候在类名后面添加<E,K,V......>
            第一种实现方式:子类在进行实现的时候,可以不填写泛型的类型,此时在创建具体的子类对象的时候才决定使用哪种类型
 */
public class FanXingDemo {

    public static void main(String[] args) {
        FanXingInterfaceSub<String> fxi = new FanXingInterfaceSub<String>();
        fxi.test2("abc");
    }
}

第二种实现

public interface FanXingInterface<B> {

    public B test();

    public void test2(B b);
}
public class FanXingInterfaceSub implements FanXingInterface<String>{//直接指定父类类型

    //实现String方法
    @Override
    public String test() {
        return null;
    }

    @Override
    public void test2(String s) {
        System.out.println(s);
    }
}
import java.util.ArrayList;
import java.util.List;
/*
       子类在实现泛型接口的时候,只在实现父类的接口的时候指定父类的泛型类型
       此时具体方法中的泛型类型必须要跟父类中的保持一致,也无需声明

 */
public class FanXingDemo {

    public static void main(String[] args) {
        FanXingInterfaceSub fxi = new FanXingInterfaceSub();//无需用<>声明,此时指定String类型
        fxi.test2("abc");//abc
    }
}
3.泛型方法
public class FanXingMethod<T> {

    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public<Q> void show(Q q){
        System.out.println(q);
        System.out.println(t);
    }
}
import java.util.ArrayList;
import java.util.List;
/*
        泛型方法
            定义方法的时候,指定方法的返回值和参数是自定义的占位符,可以使类名中的T,也可以是自定义的Q
            只不过在使用Q的时候,需要把<Q>定义在返回值的前面:
            public<Q> void show(Q q){
                System.out.println(q);
            }
 */
public class FanXingDemo {

    public static void main(String[] args) {

        FanXingMethod<String> fxm = new FanXingMethod<>();
        fxm.setT("ttt");
        fxm.show(123);
    }
}
4.泛型的上限、反省的下限(基本不会使用)
/*
泛型的上限
            如果父类确定了,所有的子类可以直接使用
泛型的下限
            如果子类确定了,子类的所有父类都可以直接传递参数使用
*/         

Map

Map基本概念

特点:key-value映射

HashMap

​ Key无序 唯一(Set)

​ Value无序 不唯一(Collection)

LinkedHashMap

​ 有序的HashMap速度快

TreeMap

​ 有序 速度没有hash快

package Map;
/*
    map存储的是k-v键值对映射的数据
        实现子类:
            HashMap:数据+链表(1.7)、数据结构+链表+红黑树(1.8之后)
            LinkedHashMap:链表
            TreeMap:红黑树

        基本api操作:
            增加:
                put(k,v):插入

            查找:
                isEmpty:判断是否非空
                size:返回map的大小
                containsKey:查找k
                containsValue:查找v
                get:查找对应k的下标

            删除:
                clear:清空集合中的所有元素
                remove:删除指定元素

            map.entry:表示的是k-v组合的一组映射关系,k-v成组出现
 */
import java.util.*;

public class MapDemo {

    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("a", 1);
        map.put("b", 2);
        map.put("c", 3);
        map.put("d", 4);
        System.out.println(map);
        System.out.println(map.isEmpty());
        System.out.println(map.size());
//        map.clear();

        System.out.println(map.containsKey("b"));
        System.out.println(map.containsValue(4));
        System.out.println(map.get("a"));

//        map.remove("a");

        System.out.println("---------------------------------------------");

        //遍历操作 
        Set<String> keys = map.keySet();
        for(String key : keys){
            System.out.println(key+" = "+map.get(key));
        }

        Collection<Integer> values = map.values();//只能根据k获取对应的v值,无法用v获取k
        for(Integer i : values){
            System.out.println(i);
        }
        
        System.out.println("---------------------------------------------");

        //迭代器
        Set<String> keys2 = map.keySet();
        Iterator<String> iterator = keys2.iterator();
        while (iterator.hasNext()){
            String key = iterator.next();
            System.out.println(key+" = "+map.get(key));
        }

        System.out.println("---------------------------------------------");

        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator1 = entries.iterator();
        while (iterator1.hasNext()){
            Map.Entry<String, Integer> next = iterator1.next();
            System.out.println(next.getKey()+" = "+next.getValue());
        }
    }
}

hashMap和HashTable的区别

​ 1.hashMap线程不安全,但效率比较高;hashTable线程安全,但效率较低

​ 2.hashMap中的k,v都可以空(null),但hashTable不允许

为什么hashMap初始值为2的N次幂?

​ 1.方便进行操作,提高效率,用“&”进行运算的效率比直接取模要高

​ 2.在扩容之后涉及到元素的迁移过程,迁移的时候只需要判断二进制的前一位是0还是1

​ 如果是0,表示新数组和旧数组的下标位置保持不变;

​ 如果是1,只需要将索引位置加上旧的数组的长度值即为新数组的下标

【面经】HashMap与ConcurrentHashMap在Jdk1.7和1.8的区别

1.HashMap

在jdk1.7之前HashMap是基于数组和链表实现的,而且采用头插法。

而jdk1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。采用尾插法。

HashMap默认的初始化大小为 16。当HashMap中的元素个数之和大于负载因子*当前容量的时候就要进行扩充,容量变为原来的 2 倍。(这里注意不是数组中的个数,而且数组中和链/树中的所有元素个数之和!)

2.ConcurrentHashMap

在jdk1.7是 分段的数组+链表 ,jdk1.8的时候跟HashMap1.8的时候一样都是基于数组+链表/红黑树。

ConcurrentHashMap是线程安全的

(1)在jdk1.7的时候是使用分段所segment,每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。

(2)在jdk1.8的时候摒弃了 Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronizedCAS 来操作。synchronized只锁定当前链表或红黑二叉树的首节点。

Collections工具类

package Collection;
/*
    Collections工具类
        addAll:批量添加
        sort:排序
        binarySearch:二分查找
        fill:替换
        shuffle:随机排序
        reverse:逆序
 */
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CollectionsDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("aau");
        list.add("bcuhue");
        list.add("caoaiw");
        list.add("dcpcowncwk");

        Collections.addAll(list, "e","faca","gacscawacavaaw");
        System.out.println(list);

        list.sort(new Comparator<String>() {//匿名内部类
            @Override
            public int compare(String o1, String o2) {
                if(o1.length()>o2.length()){
                    return 1;
                }else if(o1.length()<o2.length()){
                    return -1;
                }else
                return 0;
            }
        });
        System.out.println(list);

        //在使用二分查找的侍候需要先进行排序操作,如果数据是无序的,则找不到指定元素
        Collections.sort(list);//先排序
        System.out.println(list);
        System.out.println(Collections.binarySearch(list, "e"));

        Collections.reverse(list);
        System.out.println(list);

        Collections.fill(list, "java");
        System.out.println(list);//[java, java, java, java, java, java, java]
    }
}

Arrays工具类

package Collection;

import java.util.Arrays;
import java.util.List;

/*
    Arrays提供了数组操作的工具类,包含很多方法,比较重要的是:
        集合和数组之间的转化
 */
public class ArraysDemo {

    public static void main(String[] args) {
        //数组转成list
        int[] array = new int[]{1,2,3,4};
        List<int[]> ints = Arrays.asList(array);//转为一个数组元素

        List<Integer> integers = Arrays.asList(1, 2, 3, 4);//转为单个元素

        //list转成数组
        Object[] objects = ints.toArray();
    }
}

集合总结


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值