JavaSE进阶回顾第四天-集合

第四章

重点章节!!!

  • 主要集合概述
  • Collection 和 Iterator
  • List
  • Set
  • Map
  • Collections工具类
  • Comparable与Comparator
集合概述

总结重点:

  • 把继承结构图背会
  • 把Collection接口常用方法测试几遍
  • 把迭代弄明白
  • Collection接口中的contains方法和remove方法底层都调用了equals方法,这个弄明白。
  1. 集合不能直接存储基本数据类型,另外集合也不能直接存储Java对象,集合当中存储的都是Java对象的地址(或者说集合当中存储的是引用)。如:list.add(100);//自动装箱Integer。
    在这里插入图片描述

  2. 在Java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到不同的数据结构当中。数组、二叉树、链表、哈希表等,以上这些都是常见的数据结构。要掌握在不同情况下使用哪一种合适的集合去使用即可。

​ new ArrayList();创建一个集合,底层是数组。

​ new LinkedList();创建一个集合,底层是链表。

​ new TreeSet();创建一个集合,底层是二叉树。

  1. 集合在java JDK那个包下?

    java.util.*

    所有的集合类和集合接口都在java.util包下。

  2. 集合继承图!!!(必须掌握)

    Collection集合继承图:
    在这里插入图片描述

注意:HashSet底层是一个HashMap,TreeSet底层是一个TreeMap。

​ Map集合继承图:
在这里插入图片描述

总结(所有的实现类):

ArrayList:底层是数组,初始化容量是10,扩容因子是1.5。

LinkedList:底层是双链表。

Vector:底层是数组,线程安全的,效率较低,使用较少。

HashSet:底层是HashMap,放到HashSet中的元素等同于放到HashMap集合key部分。

TreeSet:底层是TreeMap,放到TreeSet的元素等同于放到TreeMap集合的key部分。

HashMap:底层是哈希表,初始化容量16,加载因子0.75,初始化容量必须是2的倍数,k,v允许为null。

Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少,初始化容量是11,扩容是 原容量乘以2加1,k和v都不能为null。

Properties:是线程安全的,并且key和value只能存储字符串String类型。

TreeMap:底层是二叉树,TreeMap集合的key可以自动按大小顺序排序。

List集合存储元素的特点:有序可重复。

Set(Map)集合存储元素的特点:无序不可重复。

SortedSet(SortedMap)存储元素的特点:首先是无序不可重复的,但是SortedSet集合中的元素是可排序的(可以按照大小顺序排列)。

记住:Map集合的key就是一个Set集合!!!往Set集合中放数据,实际上放到了Map集合的key部分。

Collection 和 Iterator

Collection接口常用方法:

/**
 * 关于java.util.Collection接口中常用的方法。
 * 1、Collection中能存放什么元素?
 *      没有使用“泛型”之前,Collection中可以存储Object的所有子类型。
 *      使用了“泛型”之后,Collection中只能存储某个具体的类型。
 *      集合中不能直接存储基本数据类型,也不能存java对象,只是存储java对象的内存地址。。
 * 2、Collection中的常用方法
 *      boolean add(Object e);  向集合中添加元素
 *      int size()  获取集合中元素的个数
 *      void clear()  清空集合
 *      boolean contains(Object o)  判断当前集合是否包含元素o
 *      boolean remove(Object o)  删除集合中某个元素
 *      boolean isEmpty()  判断集合中是否为空
 *      Object[] toArray();  将集合转化为数组。
 */
public class CollectionTest01 {
    public static void main(String[] args) {
        //Collection c = new Collection();//接口1完全抽象的,无法new对象。

        //多态
        Collection c = new ArrayList();

        //测试Collection接口中的常用方法。
        c.add(1200);//自动装箱(java5的新特性),实际上是放进去了一个对象的内存地址。Integer x = new Integer(1200);
        c.add(true);
        c.add(3.14);
        c.add(new Object());
        c.add(new Student());

        //获取集合元素的个数
        System.out.println("集合元素的个数:"+c.size());//5

        //清空集合
        c.clear();
        System.out.println("集合元素的个数:"+c.size());//0

        //再向集合中添加元素
        c.add("hello");//"hello"对象的内存地址放到了集合当中。
        c.add("world");
        c.add("浩克");
        c.add("绿巨人");
        c.add(1);

        //判断集合中是否包含绿巨人
        boolean flat = c.contains("绿巨人");
        System.out.println(flat);//true
        boolean flat2 = c.contains("绿巨人2");
        System.out.println(flat2);//false
        System.out.println(c.contains(1));//true

        System.out.println("集合元素的个数:"+c.size());//5

        //删除集合中某个元素
        c.remove(1);
        System.out.println("集合元素的个数:"+c.size());//4

        c.clear();
        System.out.println(c.isEmpty());//true

        c.add("abc");
        c.add("def");
        c.add(100);
        c.add("hello world");
        //转换成数组 使用不多,了解即可。
        Object[] objects = c.toArray();
        for (int i=0;i<objects.length;i++){
            System.out.println(objects[i]);
        }

    }
}
class Student{
}

Iterator迭代器:

在这里插入图片描述

在这里插入图片描述

/**
 * 关于集合遍历/迭代专题。(重点:五颗星*****)
 */
public class CollectionTest02 {
    public static void main(String[] args) {
        //注意:以下讲解的遍历方式,是所有Collection通用的一种方式。
        //在Map集合中不能用。在所有的Collection以及子类中使用。
        //创建集合对象
        Collection c = new HashSet();
        c.add("abc");
        c.add("def");
        c.add(true);
        c.add(new Object());
        
        //对集合Collection进行遍历/迭代
        //第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        //第二步:通过以上获取的迭代器对象开始迭代/遍历集合。
        /*
            以下两个方法是迭代器Iterator中的方法:
                boolean hasNext()  如果仍有元素可以迭代,则返回true。
                Object next()  返回迭代的下一个元素。
         */
        while (it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }
    }
/**
 * 关于集合的迭代/遍历
 */
public class CollectionTest03 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();//ArrayList集合有序可重复。
        //添加元素
        c.add(1);
        c.add(2);
        c.add(3);
        c.add(4);
        c.add(1);

        //迭代集合
        Iterator it = c.iterator();
        while (it.hasNext()){
            //存进去什么类型,取出来还是什么类型。
            Object obj =it.next();
            /*if (obj instanceof Integer){
                System.out.println("Integer类型");
            }*/
            //只不过在输出的时候会转换为字符串,因为println会调用toString()方法。
            System.out.println(obj);
        }

        //HashSet集合:无序不可重复。
        Collection c2 = new HashSet();
        c2.add(100);
        c2.add(200);
        c2.add(300);
        c2.add(100);
        c2.add(50);
        c2.add(20);
        c2.add(30);
        c2.add(100);
        System.out.println("HashSet元素个数:"+c2.size());//3
        Iterator it2 = c2.iterator();
        while (it2.hasNext()){
            Object obj = it2.next();
            System.out.println(obj);
        }

    }
}
/*
运行结果:
1
2
3
4
1
HashSet元素个数:6
50
100
20
200
300
30
*/
深入Collection的contains以及remove方法

在这里插入图片描述

/**
 * 深入Collection的contains方法。
 *  boolean contains(Object o)
 *      判断集合中是否包含某个对象o
 *      如果包含返回true,如果不包含返回false
 *
 *  contains方法是用来判断集合中是否包含某个元素的方法,
 *  那么它在底层到底怎样判断集合中是否包含某个元素的呢?
 *      调用了equals方法进行对比。
 *      equals方法返回true就表示包含这个元素。
 */
public class CollectionTest04 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c =new ArrayList();

        //向集合中存储元素
        String s1 = new String("abc");
        c.add(s1);

        String s2 = new String("def");
        c.add(s2);

        //集合遍历
        Iterator it = c.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        //集合元素个数
        System.out.println("集合元素个数:"+c.size());

        //新建对象String
        String x = new String("abc");

        //c集合中是否包含x? 不包含(见上面内存图) 那为什么contains以及equals比较出来都是true呢?
        System.out.println(c.contains(x));//true 因为contains方法重写了equals方法,比较的是内容是否相同!!!
        System.out.println(x.equals(s1));//true String类重写equals方法

    }
}

测试contains方法和remove方法:

/**
 * 测试contains方法和remove方法
 */
public class CollectionTest05 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        User u1 = new User("jack");
        User u2 = new User("jack");
        c.add(u1);
        //判断集合中是否包含u2
        System.out.println(c.contains(u2));//false
        //contains底层调用的equals方法,但是User类没有重写equals方法,的u1和u2比较用的是==,
        //==比较的是u1和u2保存的堆区对象地址,new了两次,地址显然不同,所以为false。

        //重写equals方法后就比较的是内容。结果为true!!!

        Integer i1 = new Integer("10000");
        c.add(i1);
        Integer i2 = new Integer("10000");
        System.out.println(c.contains(i2));//true 因为Integer类重写了equals方法!!

        //remove 方法
        Collection cc = new ArrayList();
        String s1 = new String("hello");
        cc.add(s1);
        System.out.println("集合元素个数:"+cc.size());//1
        String s2 = new String("hello");
        cc.remove(s2);//s1.equals(s2) java认为s1和s2是一样的,删除s2就是删除s1。
        System.out.println("集合元素个数:"+cc.size());//0 remove成功

    }
}
class User{
    private String name;

    public User() {
    }

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

重写equals方法后就比较的是内容。结果为true!!!

放在集合里面的元素要重写equals方法!!!这样比较的就是内容是否相等

关于集合中元素的删除:

/**
 * 关于集合元素的remove
 *      重点:当集合结构发生了改变,迭代迭必须重新获取,如果还是用以前老的迭代器,会出现
 *      异常:java.util.ConcurrentModificationException
 *
 *      重点:在迭代集合元素过程中,不能调用集合对象的remove方法,删除元素
 *      c.remove(obj);迭代过程中不能这样。
 *      会出现:java.util.ConcurrentModificationException
 *
 *      在集合迭代过程中使用迭代器删除还是可以的!!!
 *
 *      重点:在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法删除元素,
 *      不要使用集合自带的remove方法删除元素。
 */
public class CollectionTest06 {
    public static void main(String[] args) {

        //创建集合
        Collection c = new ArrayList();

        //注意:此时获取的迭代器,指向的是那个集合中没有状态的迭代器。
        //一定要注意:集合结构只要发生改变,迭代器必须重新获取。
        //当集合结构发生了改变,迭代没有重新获取时,调用next()方法时会出现java.util.ConcurrentModificationException异常
        //Iterator it =c.iterator();

        //添加元素
        c.add(1);//Integer类型
        c.add(2);
        c.add(3);


        //获取迭代器
        Iterator it = c.iterator();
        while (it.hasNext()){
            //编写next()方法返回值类型必须是Object
            Object obj = it.next();
            System.out.println(obj);
        }

        Collection c2 = new ArrayList();
        c2.add("abc");
        c2.add("def");
        c2.add("xyz");

        Iterator it2 = c2.iterator();
        while (it2.hasNext()){
            Object obj = it2.next();
            //删除元素
            //删除元素之后,集合的结构发生了变化,应该重新去获取迭代器,
            //但是,循环下一次的时候并没有重新获取迭代器,所以会出现异常。java.util.ConcurrentModificationException
            //出异常的根本原因是:集合中元素删除了,但是没有更新迭代器。
            //c2.remove(obj);

            //使用迭代器来删除可以吗? 可以
            //迭代器删除时会自动更新迭代器,并更新集合(删除集合中的元素)。
            it2.remove();//使用迭代器删除的一定是迭代器指向的当前元素。
            System.out.println(obj);
        }

        System.out.println("集合中的元素个数:"+c2.size());
    }
}
List集合常用方法
/**
 * 测试LiST接口中常用方法
 *      1、List集合存储特点:有序可重复
 *      2、List仍然是Collection的子接口,那么肯定List接口有自己"特有"的方法:
 *          以下只列出List接口特有的常用的方法:
 *          void add(int index,E element)
 *          Object get(int index)
 *          int indexOf(Object o)
 *          int lastIndexOf(Object o)
 *          Object remove(int index)
 *          Object set(int index, E element)
 *
 *
 */
public class ListTest01 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("A");//默认是想列表的尾部插入元素。
        list.add("B");
        list.add("C");
        list.add("D");

        //将KING插入到集合下标为1的位置。
        //这个方法使用不多,因为ArrayList底层是数组,效率低,需要移动大量元素。
        list.add(1,"KING");

        //迭代
        Iterator it = list.iterator();
        while (it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }
        System.out.println("============================================");

        //根据下标获取元素
        Object o = list.get(2);//B
        System.out.println(o);
        System.out.println("============================================");

        //因为有下标,所以List集合有自己比较特殊的遍历方式
        //通过下标遍历
        for(int i = 0;i<list.size();i++){
            Object object = list.get(i);
            System.out.println(object);
        }
        System.out.println("============================================");


        //获取指定对象第一次出现的索引
        System.out.println(list.indexOf("KING"));//1

        //获取指定对象最后一次出现的索引
        System.out.println(list.indexOf("C"));//3

        //删除List指定下标的元素
        list.remove(3);//删除了C
        System.out.println(list.size());//4
        System.out.println("================================");

        //修改指定位置的元素的值
        list.set(2,"Soft");//将B修改为了Soft
        //遍历
        for (int i = 0;i<list.size();i++){
            Object object = list.get(i);
            System.out.println(object);
        }

    }
}
ArrayList集合
/**
 * ArrayList集合:
 *      1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为10)
 *      2、集合底层是一个Object数组。
 *      3、构造方法:
 *          new ArrayList();
 *          new ArrayList(20);
 *      4、ArrayList集合的扩容:
 *          原容量的1.5倍。
 *          ArrayList集合底层是数组,怎么优化?
 *              尽可能少的扩容。因为数组扩容的效率比较低,建议使用ArrayList集合的
 *              时候预估以下元素的个数,给的一个初始化容量。
 *      5、数组优点:
 *          检索效率比较高。(每个元素占用的空间大小相同,内存地址连续,
 *          知道元素内存地址以及下标就可以计算出元素的内存地址,所以检索效率最高)
 *      6、数组缺点:
 *          随机增删元素效率比较低。
 *          数组无法存储大数据量。(很难找到一块非常巨大的连续的内存空间)
 *      7、向数组末尾添加元素,效率很高,不受影响。
 *      8、面试题:这么多集合中,哪个用的最多?
 *          答:ArrayList集合。
 *          因为在数组末尾添加元素,效率不受影响。
 *          另外,我们检索/查询某个元素比较多。
 *      9、ArrayList集合是非线程安全的。
 */
public class ArrayListTest01 {
    public static void main(String[] args) {
        //默认初始化容量是10
        List list1 = new ArrayList();
        //集合size()方法是获取当前集合中元素的个数,不是获取集合的容量。
        System.out.println(list1.size());//0

        //指定初始化容量
        List list2 = new ArrayList(20);
        System.out.println(list2.size());//0
    }
}

ArrayList集合的构造方法:

/**
 * 集合ArrayList的构造方法
 */
public class ArrayListTest02 {
    public static void main(String[] args) {
        //默认初始化容量10
        List myList1 = new ArrayList();

        //指定初始化容量100
        List myList2 = new ArrayList(100);

        //创建一个HashSet集合
        Collection c = new HashSet();
        //添加元素到Set集合
        c.add(100);
        c.add(200);
        c.add(500);
        c.add(900);

        //通过这个构造方法可以将HashSet集合转换成List集合
        List myList3 = new ArrayList(c);//
        for (int i =0;i<myList3.size();i++){
            Object obj = myList3.get(i);
            System.out.println(obj);
        }
    }
}
单链表数据结构

在这里插入图片描述

/**
 * 单链表中的节点。
 * 节点是单向链表中基本的单元。
 * 每个Node都有两个属性
 *      一个属性:是存储的数据
 *      另一个属性:存储的是下一个节点的内存地址。
 */
public class Node {

    //存储的数据
    Object element;

    //下一个节点的内存地址
    Node next;

    public Node() {
    }

    public Node(Object element, Node next) {
        this.element = element;
        this.next = next;
    }
}
public class Link {

    //头结点
    Node header = null;

    //向链表中添加元素
    public void add(Object data){//尾插法
        Node t = header;
        if (header==null){
            header = new Node(data,null);
        }else {
            while (t.next!=null){//获取末尾节点
                t=t.next;
            }
            t.next=new Node(data,null);//将新节点尾插法插入到链表末尾。
        }
    }

    //删除元素
    public void remove(Object data){
        /*Node t = header;
        if (header.element==data){
            header=header.next;
        }else {
            while (t.next!=null){
                if (t.next.element==data){
                    t.next = t.next.next;
                }
                t = t.next;
            }
        }*/

    }

    //修改某个元素
    public void modify(Object data){

    }

    //查找某个元素
    public int find(Object data){
        /*Node t = header;
        int cnt = 0;
        while (t!=null){
            if (t.element==data){
                return cnt;//返回下标
            }
            t=t.next;
            cnt++;
        }*/
        return -1;
    }

    public void printAll(){
        Node t = header;//第一个元素地址
        while (t!=null){
            System.out.println(t.element);
            t=t.next;
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        Link link = new Link();
        link.add(100);//添加元素
        link.add(200);
        link.add(300);
        link.add(400);
        link.add(500);
        link.printAll();
        System.out.println("======================================");
        /*link.remove(300);//删除元素
        link.printAll();
        System.out.println("======================================");
        int index = link.find(200);
        System.out.println("index:="+index);*/
    }
}

链表的优缺点:

在这里插入图片描述

在这里插入图片描述

LinkedList集合
Vector
/**
 * Vector:
 *      1、底层也是一个数组。
 *      2初始化容量:10
 *      3、怎么扩容的?
 *          扩容之后是原容量的2倍。
 *          10-->20-->40-->80
 *      4、ArrayList集合扩容特点:
 *          扩容之后是原容量1.5倍
 *          10-->15->15*1.5
 *      5、Vector中所有的方法都是线程同步的,都带有synchronized关键字,
 *      是线程安全的。
 *      6、怎么将一个线程不安全的ArrayList集合转换为线程安全的呢?
 *      使用集合工具类:
 *          java.util.Collection;
 *
 *          java.util.Collection 是集合接口
 *          java.util.Collections 是集合工具类
 *
 *
 */
public class VectorTest {
    public static void main(String[] args) {
        //创建一个Vector集合
        List v = new Vector();//使用多态
        //Vector v = new Vector();

        //添加元素
        //默认容量是10个
        v.add(1);
        v.add(2);
        v.add(3);
        v.add(4);
        v.add(5);
        v.add(6);
        v.add(7);
        v.add(8);
        v.add(9);
        v.add(10);

        //满了之后扩容,扩容之后的容量是20.
        v.add(11);

        Iterator it = v.iterator();
        while (it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        //这个可能以后要使用
        List myList = new ArrayList();//非线程安全的。
        //变成线程安全的
        Collections.synchronizedList(myList);

        //这样myList集合就是线程安全的。
        myList.add("111");
        myList.add("222");
        myList.add("333");

    }
}
泛型机制
/**
 *1、 JDK5.0之后推出的新特性:泛型
 *2、泛型这种语法机制,只在程序编译阶段起作用,值是给编译器参考的。(运行阶段泛型没用!)
 *3、使用泛型有什么好处?
 *  第一:集合中存储的元素类型统一了。
 *  第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的强制类型转换了。
 *4、泛型的缺点是什么?
 *  导致集合中存储的元素缺乏多样性。
 *  大多数业务中,集合的类型还是统一的,所这种泛型特性被大家所认可。
 *
 */
public class GenericTest01 {
    public static void main(String[] args) {

        /*
        //不使用泛型机制,分析程序存在的缺点。
        List myList = new ArrayList();

        //准备对象
        Cat c = new Cat();
        Bird b = new Bird();

        //将对象添加到集合当中
        myList.add(c);
        myList.add(b);

        //遍历集合,取出猫让它抓老鼠,取出鸟让它飞
        Iterator it = myList.iterator();
        while (it.hasNext()){
            Object obj =it.next();
            if (obj instanceof Cat){//向下转型
                ((Cat)obj).catchMouse();
            }
            if (obj instanceof Bird){//向下转型
                ((Bird)obj).fly();
            }
        }
         */

        //使用JDK5之后的泛型机制
        //使用泛型List<Animal>之后,表示List集合只允许存储Animal类型的数据。
        List<Animal> myList = new ArrayList<Animal>();
        myList.add(new Cat());
        myList.add(new Bird());

        //获取迭代器
        //这个表示迭代器迭代的是Animal类型。
        Iterator<Animal> it = myList.iterator();
        while (it.hasNext()){
            //使用泛型之后,每次迭代返回的数据都是Animal类型。
            Animal a = it.next();
            //这里不需要进行强制类型转换。直接调用。
            a.move();

            //调用子类特有方法还是需要向下转型的!!!
        }
    }
}

class Animal{
    //父类自带方法
    public void move(){
        System.out.println("动物在移动!");
    }
}

class Cat extends Animal{
    //特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠!");
    }
}

class Bird extends Animal{
    //特有方法
    public void fly(){
        System.out.println("鸟儿在飞翔!");
    }
}
/**
 *JDK8之后引入了:自动类型推断机制。(又称为钻石表达式<>)
 */
public class GenericTest02 {
    public static void main(String[] args) {
        //ArrayList<这里的类型会自推断>(),前提是JDK8之后才允许。
        List<Animal> myList = new ArrayList<>();

        myList.add(new Animal());
        myList.add(new Bird());
        myList.add(new Cat());

        //遍历
        Iterator<Animal> it = myList.iterator();
        while (it.hasNext()){
            Animal a = it.next();
            a.move();
        }

        List<String> myList2 = new ArrayList<>();

        //类型不匹配。
        //myList2.add(new Cat());

        myList2.add("http://www.126.com");
        myList2.add("http://www.baidu.com");
        myList2.add("http://www.qq.com");

        //类型不匹配。
        //myList2.add(123);

        System.out.println(myList2.size());

        //遍历
        Iterator<String> it2 = myList2.iterator();
        while (it2.hasNext()){
            String str = it2.next();
            System.out.println(str.substring(7));
        }
    }
}
foreach
/**
 * JDK5.0之后推出了一个新特性:foreach
 */
public class ForEachTest01 {
    public static void main(String[] args) {
        int[] arr = {1,200,8,500,36,99,124};

        //
        for (int i = 0;i < arr.length;i++){
            System.out.println(arr[i]);
        }
        System.out.println("===============================");

        //foreach方式
        for(int data:arr){
            System.out.println(data);
        }

    }
}
/**
 * 集合使用foreach
 */
public class forEachTest02 {
    public static void main(String[] args) {
        //创建List集合
        List<String > list = new ArrayList<>();
        //添加元素
        list.add("hello");
        list.add("ketty");
        list.add("world");

        //遍历,使用迭代器方式
        Iterator<String> it =list.iterator();
        while (it.hasNext()){
            String str =it.next();
            System.out.println(str);
        }
        System.out.println("===============================");

        //遍历,使用下标的方式
        for (int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        System.out.println("===============================");

        //遍历,使用foreach方式(推荐使用!!!)
        for (String data:list){
            System.out.println(data);
        }
        System.out.println("===============================");

        List<Integer> list1 = new ArrayList<>();
        list1.add(100);
        list1.add(200);
        list1.add(300);
        for (Integer data:list1){//data代表集合中的元素
            System.out.println(data);
        }

    }
}
HashSet集合
/**
 * HashSet集合:
 *      无序不可重复
 */
public class HashSetTest01 {
    public static void main(String[] args) {
        //演示一下HashSet集合特点
        Set<String> strs = new HashSet<>();

        //添加元素
        strs.add("hello3");
        strs.add("hello3");
        strs.add("hello1");
        strs.add("hello2");
        strs.add("hello3");
        System.out.println(strs.size());

        //遍历
        /*
        3
        hello1
        hello2
        hello3
        1、存储时顺序和取出的顺序不同。
        2、不可重复。
        3、放到HashSet集合的元素实际上是放到了HashMa集合的key部分。
         */
        for (String data:strs){
            System.out.println(data);
        }

    }
}
TreeSet集合
/**
 *TreeSet集合存储元素特点:
 * 1、无序不可重复,但是存储的元素可以自动按照大小顺序排序,又称为可排序集合。
 */
public class TreeSetTest01 {
    public static void main(String[] args) {
        Set<String> set = new TreeSet();
        set.add("222");
        set.add("222");
        set.add("222");
        set.add("111");
        set.add("333");
        set.add("111");
        set.add("111");
        set.add("111");
        //遍历
        for (String data:set){
            System.out.println(data);
        }
    }
}
/*
结果:
111
222
333
*/

在这里插入图片描述

/**
 * 1、TreeSet集合底层实际上是一个TreeMap
 * 2、TreeMap集合底层是一个二叉树。
 * 3、放到TreeSet集合的元素,等同于放到TreeMap集合的key部分
 * 4、TreeSet集合中的元素:无序不可重复,但是按元素的大小顺序自动排序。
 * 称为:可排序集合
 */
public class TreeSetTest02 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        Set<String> set = new TreeSet<>();

        //添加元素
        set.add("1999");
        set.add("1996");
        set.add("2000");
        set.add("1997");

        //遍历
        for (String data:set){//有序的
            System.out.println(data);
        }
    }
}
/**
 * 对于自定义类型TreeSet可以自动排序吗?
 *      以下程序对于Person类型来说,无法排序。因为没有指定Person对象的比较规则
 *      谁大谁小并没有说明啊。
 *
 *      以下程序运行时会出现这个异常:
 *          java.lang.ClassCastException
 *          com.chinasoft.javase.set.Person
 *          cannot be cast to java.lang.Comparable
 *      出现这个异常的原因是:
 *          Person类没有实现java.util.Comparable接口
 */
public class TreeSetTest03 {
    public static void main(String[] args) {
        TreeSet<Person> ts = new TreeSet<>();
        Person p1 = new Person(32);
        Person p2 = new Person(28);
        Person p3 = new Person(48);
        Person p4 = new Person(18);

        ts.add(p1);
        ts.add(p2);
        ts.add(p3);
        ts.add(p4);

        for (Person data:ts){
            System.out.println(data);
        }

    }
}
class Person{
    int age;

    public Person(int age) {
        this.age = age;
    }
}
Map集合常用方法
/**
/**
 * java.util.Map接口中的常用方法:
 *      1、Map和Collection没有继承关系。
 *      2、Map集合以key和value的方式存储数据:键值对。
 *          key和value都是引用数据类型。
 *          key和value都是存储对象的内存地址。
 *          key起到主导的地位,value是key的一个附属品。
 *      3、Map接口中常用的方法:
 *          V put(K key,V value); 向集合中添加元素
 *          V get(Object key); 通过key获取value
 *          void clear(); 清空Map集合
 *          boolean containsKey(Object key); 判断Map集合是否包含某个key
 *          boolean containsValue(Object value); 判断Map集合是否包含某个value
 *          boolean isEmpty(); 判断Map集合中元素个数是否为0
 *          Set<K> keySet(); 获取Map集合所有的key(所有的键是一个Set集合)
 *
 *          V remove(K key,V value); 通过key删除元素
 *          int size();
 *          Collection<V> values(); 获取Map集合中所有的value,返回一个Collection
 *
 *          Set<Map.Entry<K,V>> entrySet();
 *              将Map结合转换为Set集合
 *              假设现在有一个Map集合,如下所示:
 *              map1集合
 *              key          value
 *              ----------------------
 *              1            zhangsan
 *              2            lisi
 *              3            wangwu
 *              4            zhaoliu
 *              Set set = map1.entrySet();
 *              set集合对象:
 *              1=zhangsan 【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是Map.Entry<K,V>】
 *              2=lisi 【Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类】              		  *			      3=wangwu
 *              4=zhaoliu --->这个东西是个什么? Map.Entry
 *
 */
public class MapTest01 {
    public static void main(String[] args) {
        //创建Map对象
        Map<Integer,String> map = new HashMap<>();
        //向Map中添加键值对
        map.put(1, "aaa");//key在这里自动装箱
        map.put(2, "bbb");
        map.put(3, "ddd");
        map.put(4, "ccc");

        //通过key获取value
        String value = map.get(3);
        System.out.println(value);

        //获取键值对数量
        System.out.println("键值对的数量:"+map.size());//4

        //contains方法底层调用的都是equals方法,所以自定义的类型需要重新equals方法。
        //通过key删除键值对
        map.remove(4);
        System.out.println("键值对的数量:"+map.size());//3

        //判断是否包含某个key
        System.out.println(map.containsKey(2));//true

        //判断是否包含某个value
        System.out.println(map.containsValue("aaa"));//true

        //获取所有的value
        Collection<String> c = map.values();
        for (String data:c){
            System.out.println(data);
        }

        //清空集合
        map.clear();
        System.out.println("键值对的数量:"+map.size());//0

    }
}
/*
结果:
ddd
键值对的数量:4
键值对的数量:3
true
true
aaa
bbb
ddd
键值对的数量:0
*/

静态内部类:

/**
 * 静态内部类
 */
public class MyClass {
    //声明一个静态内部类
    private static class InnerClass{
        //静态方法
        public static void m1(){
            System.out.println("静态内部类的m1方法执行");
        }

        //实例方法
        public void m2(){
            System.out.println("静态内部类的实例方法m2执行!");
        }
    }
    public static void main(String[] args) {

        //类名叫做:MyClass.InnerClass
        MyClass.InnerClass.m1();//调用静态内部类的静态方法

        MyClass.InnerClass mi = new MyClass.InnerClass();
        mi.m2();//调用静态内部类的实例方法

        //给一个set集合
        //该set集合中存储的对象是:MyClass.InnerClass类型
        Set<MyClass.InnerClass> set = new HashSet<>();
        set.add(new MyClass.InnerClass());
        set.add(new MyClass.InnerClass());
        //set.add("12300");//类型不匹配
        System.out.println(set.size());
        for (MyClass.InnerClass data:set){
            System.out.println(data);
        }
        System.out.println("======================================");

        //<>里面是类型,这里是 一个静态内部类类型加上了泛型
        Set<MyClass.MyEntry<String,Integer>> set1 = new HashSet<>();
        set1.add(new MyClass.MyEntry<String, Integer>());
        set1.add(new MyClass.MyEntry<String, Integer>());
        //set1.add("aaa");//类型不匹配
        System.out.println(set1.size());
        for (MyClass.MyEntry data:set1){
            System.out.println(data);
        }

    }
    public static class MyEntry<K,V>{

    }
}
/*
结果:
静态内部类的m1方法执行
静态内部类的实例方法m2执行!
2
com.chinasoft.javase.map.MyClass$InnerClass@4554617c
com.chinasoft.javase.map.MyClass$InnerClass@1b6d3586
======================================
2
com.chinasoft.javase.map.MyClass$MyEntry@74a14482
com.chinasoft.javase.map.MyClass$MyEntry@1540e19d
*/
Map集合的遍历

必须掌握!!!

方式二内存图(将map元素放到set集合):

在这里插入图片描述

/**
 * Map集合的遍历。
 *      第一种方式:获取所有的key,通过遍历key,来遍历value(适合小数据量)
 *      第二种方式:Set<Map.Entry<K,V>> entrySet();(适合大数据量,效率高,相比于第一种,不需要先查keys集合)
 *      第二种方式效率高!!!
 *
 */
public class MapTest02 {
    public static void main(String[] args) {
        //第一种方式:获取所有的key,通过遍历key,来遍历value
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"hello");
        map.put(2, "ketty");
        map.put(6, "lisi");
        map.put(3, "王五");
        Set<Integer> keys = map.keySet();

        //foreach方式遍历
        for (Integer key:keys){
            System.out.println("key:"+key+" value:"+map.get(key));
        }
        System.out.println("==============================");

        //迭代器方式遍历
        Iterator<Integer> it = keys.iterator();
        while (it.hasNext()){
            Integer key =it.next();
            System.out.println("key:"+key+" value:"+map.get(key));
        }
        System.out.println("=====================================");

        //第二种方式:Set<Map.Entry<K,V>> entrySet();
        //该方法是把Map集合直接转换成Set集合
        //Set集合中的元素是Map.Entry
        Set<Map.Entry<Integer,String>> set = map.entrySet();

        //foreach遍历方式
        for (Map.Entry<Integer,String> data:set){
            System.out.println("key:"+data.getKey()+" value:"+data.getValue());
        }
        System.out.println("==========================");

        //迭代器遍历方式
        //效率高,适合大数据量
        Iterator<Map.Entry<Integer,String>> it2 =set.iterator();
        while (it2.hasNext()){
            Map.Entry<Integer,String> me = it2.next();
            System.out.println(me);
        }
    }
}
/*
结果:
key:1 value:hello
key:2 value:ketty
key:3 value:王五
key:6 value:lisi
==============================
key:1 value:hello
key:2 value:ketty
key:3 value:王五
key:6 value:lisi
=====================================
key:1 value:hello
key:2 value:ketty
key:3 value:王五
key:6 value:lisi
==========================
1=hello
2=ketty
3=王五
6=lisi
*/
哈希表

在这里插入图片描述

/**
 * HashMap集合:
 *      1、HashMap集合底层是哈希表/散列的数据结构。
 *      2、哈希表是一个怎样的数据结构?
 *          哈希表是一个数组和单链表的结合体
 *          数组:在查询方面效率较高,随机增删方面效率很低。
 *          单向链表:在随机增删方面效率较高,在查询方面效率较低。
 *          哈希表将以上两种数据结构融合在一起,充分发挥了各自的优点。
 *      3、HashMap集合底层的源代码:
 *      public class HashMap{
 *          //HashMap底层实际上就是一个数组(一维数组)
 *          Node<K,V>[] table;
 *
 *          //静态内部类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指针,指向下一个元素。
 *          }
 *          哈希表/散列表:一维数组,这个数组中每一个元素是一个单项链表。(数组和链表的结合体)
 *      4、最主要掌握的是:
 *          map.put(k,v)
 *          map.get(k)
 *          以上两个方法的实现原理是必须掌握的。
 *      5、HashMap集合key部分特点:
 *          无序不可重复。
 *          为什么无序?因为不一定挂到哪个单向链表上。
 *          不可重复是怎么保证的?equals方法来保证HashMap集合的key不可重复。
 *          如果key重复了,value会覆盖掉。
 *
 *          放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
 *          所以HashSet集合中的元素也需要重写hashCode()和equals()方法。
 *       6、假设将哈希表的hashCode()方法返回值固定为某个值,那么就与纯单链表没有区别.
 *       这种情况散列不均匀,无法发挥散列表的优点。
 *          什么是分布均匀?
 *              假设100个元素,10个单向联邦,那么每个联邦有10个元素,这就是最好的情况
 *              ,散列分布均匀。
 *          假设将每个元素的hashCode()值都返回不一样的,那么就与纯数组没有区别,散列分布不均匀,
 *          无法发挥散列表的优点。
 *
 *          散列分布均匀需要你在重新hashCode()方法的时候有一定的技巧。
 *       7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,
 *       需要同时重写hashCode()方法和equals()方法!!
 *
 *       8、HashMap集合的初始化容量是16,默认加载因子是0.75
 *          这个加载因子是当HashMap集合底层数组容量达到75%的时候,数组开始扩容。
 *
 *       9、HashMap集合初始化的时候,初始化容量必须是2的倍数,这也是官方推荐的,
 *       专硕因为达到散列均匀,为了提高HashMap集合的存储效率,所必须的。
 */
public class HashSetTest02 {
    public static void main(String[] args) {
        //测试HashMap集合key部分的元素特点
        //Integer是key,它的hashCode()方法和equals()方法都重写了。
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"aaa");
        map.put(2, "bbb");
        map.put(3, "ccc");
        map.put(2, "wanglei");//key值一样,更新value即可。
        System.out.println(map.size());

        //遍历
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        for (Map.Entry<Integer,String> data:set){
            //验证结果:HashMap集合key部分无序不可重复。
            System.out.println("key:"+data.getKey()+" value:"+data.getValue());
        }
    }
}
/**
 *1、向Map集合中存,以及向Map集合中取,都是先调用key的hashCode方法,然后
 * 再调用equals方法。
 * equals方法可能调用,也有可能不调用。
 *      拿put(k,v)举例,什么时候equals不会调用?
 *          k.hashCode()方法返回哈希值
 *          哈希值讲经过哈希算法转换成数组下标
 *          数组下标位置上是null,equals不需要执行。
 *       拿get(k) 举例,什么时候equals方法不会调用?
 *          如果数组下标位置上是null。不需要调用equals方法。
 *
 * 2、如果一个类的equals方法重写了,那么hashCode()方法必须重写。
 * 
 */
public class HashMapTest02 {
    public static void main(String[] args) {
        Student s1 = new Student("张三");
        Student s2 = new Student("张三");
        System.out.println(s1.equals(s2));
        //false 没有重写equals方法
        //重写equals之后是true

        //不重写hashCode()方法,s1和s2的hashCode肯定不一样!!!
        //重写了hashCode()方法,就一样了
        System.out.println("s1的hashCode="+s1.hashCode());//460141958
        System.out.println("s2的hashCode="+s2.hashCode());//1163157884

        //s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么
        //往HashMap集合中放的话,只能放进去个。(HashSet集合特点:无序不可重复)
        Set<Student> set = new HashSet<>();
        set.add(s1);
        set.add(s2);
        //如果不重写hashCode()方法,那么就是2个元素了!!就不符合hashSet集合的存储特点了。
        System.out.println(set.size());//1

    }
}
/**
 * HashMap集合key部分允许为null吗? 允许
 */
public class HashMapTest03 {
    public static void main(String[] args) {
        Map map = new HashMap();

        //HashMap集合允许key为空
        map.put(null, null);
        System.out.println(map.size());//1

        //
        map.put(null, "123");
        System.out.println(map.size());//1

        System.out.println(map.get(null));//123
    }
}
Properties
/**
 * 1、目前只需要掌握Properties属性类对象的相关方法即可。
 * 2、Properties是一个Map集合,继承Hashtable,Properties
 *    的key和value都是String类型。
 * 3、Properties被称为属性类对象。
 * 4、Properties是线程安全的。
 */
public class PropertiesTest01 {
    public static void main(String[] args) {
        //创建一个Properties对象
        Properties p = new Properties();
        //需要掌握Properties的两个方法,一个存,一个取。
        p.setProperty("name", "王磊");
        p.setProperty("url","jdbc:mysql://localhost:3306/chinasoft");
        System.out.println(p.getProperty("name"));
        System.out.println(p.getProperty("url"));
    }
}
自定义类型实现Comparable接口

TreeSet和TreeMap是自平衡二叉树:
在这里插入图片描述

方式一:通过实现Comparable接口,并重写compareTo()方法

public class TreeSetTest04 {
    public static void main(String[] args) {
        TreeSet<Customer> ts = new TreeSet<>();
        Customer c1 = new Customer(32);
        Customer c2 = new Customer(28);
        Customer c3 = new Customer(48);
        Customer c4 = new Customer(18);

        ts.add(c1);
        ts.add(c2);
        ts.add(c3);
        ts.add(c4);

        for (Customer data:ts){
            System.out.println(data.age);
        }

    }
}
//放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
//并且实现compareTo方法。equals可以不写。
class Customer implements Comparable<Customer>{
    int age;

    public Customer(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Customer c) {
        return this.age-c.age;//
    }
}

方式二:通过自定义比较器并实现Comparator接口

/**
 * TreesSet集合元素可排序的第二种方式:使用比较器的方式。
 *
 * Comparable和Comparator怎么选择呢?
 *      当比较规则不会发生改变的时候,或者说当比较规则只有一个的时候,建议使用Comparable接口。
 *      如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
 *
 */
public class TreeSetTest06 {
    public static void main(String[] args) {
        //Set<WuGui> set = new TreeSet<>();//这样不行,没有通过构造方法传递一个比较器。
        //Set<WuGui> set = new TreeSet<>(new WuGuiComparator());//通过构造方法传比较器

        //这里也可以使用匿名内部类的方式new比较器。
        Set<WuGui> set = new TreeSet<>(new Comparator<WuGui>() {
            @Override
            public int compare(WuGui o1, WuGui o2) {
                return o1.age-o2.age;//升序
            }
        });

        set.add(new WuGui(24));
        set.add(new WuGui(18));
        set.add(new WuGui(36));
        set.add(new WuGui(16));

        for (WuGui data:set){
            System.out.println(data.age);
        }
    }
}
//乌龟
class WuGui{
    int age;

    public WuGui(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "WuGui{" +···
                "age=" + age +
                '}';
    }
}

//单独在这里编写一个比较器
//比较器java.util.Comparator接口。(Comparable是java.lang包下的《Comparator是java.util下的)。
class WuGuiComparator implements Comparator<WuGui> {

    @Override
    public int compare(WuGui o1, WuGui o2) {
        //指定比较规则
        //按照年龄排序
        return o1.age-o2.age;//升序
    }
}
Collections工具类
/**
 * java.util.Collection 集合接口
 * java.util.Collections 集合工具类,方便集合的操作。
 */
public class CollectionsTest01 {
    public static void main(String[] args) {
        //ArrayList不是线程安全的
        List<String> list = new ArrayList<>();

        //1、将ArrayList变成线程安全的。
        Collections.synchronizedList(list);

        //2、给List集合排序
        //注意:如果是自定义类型,需要实现Comparable接口或者编写Comparator比较器。
        list.add("abz");
        list.add("abe");
        list.add("abf");
        list.add("abx");
        Collections.sort(list);//这个sort方法要求元素类型必须实现Comparable接口,否则会报错。
        for (String data:list){
            System.out.println(data);
        }

        //对Set集合怎么排序?
        Set<String> set = new HashSet<>();
        set.add("king");
        set.add("kingsoft");
        set.add("king2");
        set.add("king1");
        //Collections.sort(set);//报错,只能传List

        //将Set集合转化为List集合
        List<String> myList = new ArrayList<>(set);
        Collections.sort(myList);
        for (String data:myList){
            System.out.println(data);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值