java 集合构架 Collection( List(ArrayList、LinkedList、Vector) ,Set(HashSet、TreeSet)),Map(HashMap、Hashtabe)

由于标题写不下了,那接下来我就来一一介绍吧。( 注意:重要的相关知识或者解释我都写到注释里了,便于大家对源码的解读)

首先让我们通过一个例子来看看 单例集合Collection与双列集合Map的区别吧:

package jihe.Collection_.com;

import java.util.ArrayList;
import java.util.HashMap;

@SuppressWarnings({"all"})//镇压警告
public class Collection_ {
    public static void main(String[] args) {
        //1. 集合主要是两组(单列集合,双列集合)
        //2. Collection 接口有两个重要的子接口 List 和 Set ,他们的子类都是单列集合
        //3. Map 接口的实现子类 是双列集合,存放的 K-V
        // Collection
        // Map
        ArrayList arrayList = new ArrayList();
        arrayList.add("风煞");
        arrayList.add("tom");
        arrayList.add(100);
        System.out.println("arrayList="+arrayList);

        HashMap hashMap = new HashMap();
        hashMap.put("第一","北京");
        hashMap.put("第二","上海");
        hashMap.put("第三","深圳");
        System.out.println("hashMap="+hashMap);

    }
}

那么通过对单例集合Collection与双列集合Map的区别的简单了解,接下来先让我们来对Collection单列集合进行进一步了解吧

那我们先来看一下Collection 子接口及实现类的类图关系吧:

首先来看一下Collection 接口常用方法:

package jihe.Collection_.com;

// Collection 接口常用方法
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings({"all"})
public class Collection_01 {
    public static void main(String[] args) {
        List list=new ArrayList();
        // 1. add:添加单个元素  都是对象
        list.add("majunyi");
        list.add(124);// 相当于 list.add( new Integer(124) );都是对象
        list.add(true);
        System.out.println("list="+list);

        // 2. remove:删除指定元素
       // list.remove(124);  //删除某个指定对象元素  返回布尔值
        list.remove(0);//删除第一个元素
        System.out.println("list="+list);

        // 3.contains:查找元素是否存在
       System.out.println( list.contains(true));//true

        // 4. size:获取元素个数
        System.out.println(list.size());// 2

        // 5.isEmpty:判断是否为空
        System.out.println(list.isEmpty());// false

        // 6. clear:清空
        list.clear();
        System.out.println("list="+list);// list=[]

        // 7. addAll:添加多个元素
       List list1 = new ArrayList();
       list1.add("中国");
       list1.add("majunyi");
       list1.add(100);
       list1.add(false);
       list.addAll(list1);
       System.out.println("list="+list);// list=[中国, majunyi, 100, false]

        // 8.containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list1));// ture

        // 9. removeAll:删除多个元素
        list.add("张祎");
        list.removeAll(list1);
        System.out.println("list="+list);// 张祎

        // 说明: 以上 ArrayList 实现类来演示
    }
}

接下来让我们看一看Collection 子接口实现类的遍历方法-1.迭代器Iterator  2.增强 for 3.普通 for :

package jihe.Collection_.com;

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

// Collection 迭代器 Iterator 遍历集合

@SuppressWarnings({"all"})
public class CollectionIterator {
    public static void main(String[] args) {

        List col =new ArrayList();

        col.add(new Book("三国演义","罗贯中",10.2));
        col.add(new Book("小李飞刀","古龙",7.4));
        col.add(new Book("红楼梦","曹雪芹",15.7));

       // col.add("majunyi"); 存的是对象,该句并不报错
        // System.out.println("col="+col);
        //现在我希望能够遍历col 集合
        //1. 先得到 col 的迭代器
        Iterator iterator= col.iterator();
        //2. 使用 while 循环遍历集合 col
        while(iterator.hasNext()){//判断是否还有数据
            //返回下一个元素,类型是Object
            Object obj=iterator.next();//编译类型为 Object
            System.out.println("obj="+obj);// 运行类型为Book  多态问题
            // 或者直接 System.out.println(iterator.next()); 也没有问题
        }
        //教大家一个快捷键 迅速生成 while 循环   输入 itit
        //或者 敲 Ctrl+j 选取当前的 while 循环


        //3. 当退出 while 循环后 此时 iterator 迭代器,指向最后一个对象元素
        //不能再 iterator.next() 否则将抛出异常
        //如果想再次迭代遍历集合 需要对迭代器进行重置
        //即 iterator=col.iterator(); 即可进行第二次遍历

        System.out.println("\n\n\n");
        //另外还有一种简化的迭代器
        //使用 增强 for 循环遍历 Collection 集合
        for (Object obj:col) {
            System.out.println("obj="+obj);
        }
        //同样 教大家一个快捷键 迅速生成增强 for 循环  输入 I 直接回车
        //当然你也可以 敲 Ctrl+j 选取当前的增强 for 循环

        System.out.println("\n\n\n");
        //普通for
        for (int i = 0; i < col.size(); i++) {
            Object o=col.get(i);
            System.out.println("o="+o);
        }
    }
}

@SuppressWarnings({"all"})
class Book{
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

接下来我用一个双向链表来为大家模仿一下LinkedList 的底层结构:

package jihe.Collection_.com;
@SuppressWarnings({"all"})
public class CollectionLinkedList {
    public static void main(String[] args) {
        //模拟一个双向链表
        Node tom = new Node("tom");
        Node majunyi = new Node("majunyi");
        Node jake = new Node("jake");


        //连接三个节点,形成双向链表
        //tom->majunyi->jake
        tom.next=majunyi;
        majunyi.next=jake;
        //jake->majunyi->tom
        jake.prve=majunyi;
        majunyi.prve=tom;
        //fist and last
        Node fist=tom;//头节点
        Node last=jake;//尾节点



        //在tom 和 majunyi 之间添加一个 zhangfei
        Node zhangfei = new Node("zhangfei");
        tom.next=zhangfei;//tom->zhangfei
        zhangfei.prve=tom;//zhangfei->tom
        zhangfei.next=majunyi;//zhangfei->majunyi
        majunyi.prve=zhangfei;//majunyi->zhangfei


        //删除 majunyi
        zhangfei.next=jake;
        jake.prve=zhangfei;

        //演示遍历
        //从头到尾
       while (true){
           if(fist==null){
               break;
           }
           else{
               System.out.println(fist);
               fist=fist.next;
           }
       }
       System.out.println("\n\n\n");
       //从尾到头
        while (true){
            if(last==null){
                break;
            }
            else{
                System.out.println(last);
                last=last.prve;
            }
        }
    }
}

// 定义一个 Node 类,Node 对象,表示双向链表的一个节点
class Node{
    public Object item;//真正存放数据
    public Node next;//指向下一个节点
    public Node prve;//指向上一个节点
    public Node(Object name){
        this.item=name;
    }
    public String toString(){
        return "Node name="+item;
    }
}

接下来让我们来看看List接口实现类的主要方法:

package jihe.Collection_.com;

// List 中的常用方法
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings({"all"})//镇压警告
public class CollectionList {
    public static void main(String[] args) {
        List list =new ArrayList();

        // 1. List接口实现类 是有序的 而且对象元素是可以重复的 存取顺序相同
        // add:添加方法
        list.add("jake");
        list.add("majunyi");
        list.add("mary");
        list.add("mary");//可以重复
        System.out.println("list="+list);
        //另外还有 addAll:添加多个元素
        List list1 = new ArrayList();
        list1.add("中国");
        list1.add(100);
        list1.add(false);
        list.addAll(1,list1);
        System.out.println("list="+list);// 输出 list=[jake, 中国, 100, false, majunyi, mary, mary]
        // 相当于从第二个元素开始插入

        // 2. List 集合中的每一个元素都有其对应的顺序索引,即支持索引
        // Object get(int i) :取出第i+1个元素
        System.out.println(list.get(1));//取出第二个元素

        // 3. int indexOf(Object obj) :返回obj 在集合中第一次出现的位置
        System.out.println(list.indexOf("majunyi"));// 4

        // 4. int lastIndexOf(Object obj):返回obj 在集合中最后一次出现的位置
        System.out.println(list.lastIndexOf("mary"));// 6

        // 5. Object remove(int index):移除指定位置的元素
        list.remove(6);
        System.out.println("list="+list);// 输出 list=[jake, 中国, 100, false, majunyi, mary]

        // 6. Object set(int index,Object obj):设置指定index 位置的元素为obj ,相当于是替换
        // 注意:不能越界使用
        list.set(3,true);
        System.out.println("list="+list);// 输出 list=[jake, 中国, 100, true, majunyi, mary]

        // 7. List subList(int formIndex, int toIndex): 返回从 forIndex 到 toIndex位置的子集合
        List list2 = list.subList(1, 5);
        System.out.println("list2="+list2);// 输出 list2=[中国, 100, true, majunyi]
        //也就是说 subList(int formIndex, int toIndex) 返回的元素并不包含 toIndex位置的元素,
        // 可以理解为返回 [formIndex,toIndex) 区间位置的元素

    }
}

另外让我们来了解一下Set接口实现类的相关性质:

package jihe.Collection_.com;

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

//Collection 中 Set 集合是无序的,且对象元素不能重复,也没有索引,即无法通过索引来获取元素,最多只能有一个空值
//方法和Collection接口相同,迭代器都相同 Iterator
// HashSet, LinkedHashSet,TreeSet
@SuppressWarnings({"all"})
public class CollectionSet_ {
    public static void main(String[] args) {
        Set set=new HashSet();
        set.add("ject");
        set.add("majunyi");
        set.add(true);
        set.add(null);
        set.add(168);
        System.out.println("set="+set);//输出 set=[null, majunyi, 168, ject, true]
        //可以看出取出顺序和添加顺序不一致
        // 但他的去除顺序是固定的也就是 下一次取出显示也为  set=[null, majunyi, 168, ject, true]
        set.add("zhangfei");
        System.out.println("set="+set+"\n\n\n");
        set.remove("zhangfei");

        //迭代器
        Iterator iterator= set.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("next="+next);
           // System.out.println(iterator.next());
        }
        System.out.println("\n\n\n");

        //增强for 遍历
        for (Object o :set) {
            System.out.println(o);
        }

        //set 不能用普通for 遍历
    }
}

同样我来为大家演示一下HashSet的底层Hashtabe的底层机制:(这里我建议大家去追一遍,顺便了解一下Set接口的实现类不能重复的原因和标准):

package jihe.Collection_.com;
@SuppressWarnings({"all"})
public class HashSet_ {
    public static void main(String[] args) {
        //模拟 HashSet->HashMap的底层 数组+单向链表+红黑树

        // 1.创建一个数组,数据的类型是 Noed_
        Noed_[] tabe = new Noed_[16];
        System.out.println("tabe="+tabe);
        // 2. 将john放到 tabe[2]
        Noed_ john=new Noed_("john",null);
        tabe[2]=john;
        Noed_ jact=new Noed_("jact",null);
        john.next=jact;//john->jact
        Noed_ rose = new Noed_("rose",null);
        jact.next=rose;//jact->rose
        System.out.println("tabe="+tabe[2]);

        Noed_ lucy=new Noed_("lucy",null);
        tabe[1]=lucy;
        Noed_ majunyi=new Noed_("majunyi",null);
        lucy.next=majunyi;
        System.out.println("tabe="+tabe[1]);
    }
}



class Noed_{//节点,存储数据,可以指向下一个节点,从而形成链表(单项链表)
    Object item;//存放数据
    Noed_ next;//指向下一个节点

    public Noed_(Object item, Noed_ next) {
        this.item = item;
        this.next = next;
    }


    @Override
    public String toString() {
        return "Noed_{" +
                "item=" + item +
                ", next=" + next +
                '}';
    }
}

下面让我们来看看HashSet 元素加入的规则:

package jihe.Collection_.com;
//Set 添加
import java.util.HashSet;
import java.util.Set;

@SuppressWarnings({"all"})

//HashSet 的底层是HashMap,HashMap 的底层是(数组+单向链表+红黑树)
public class Set_HashSet {
    public static void main(String[] args) {
        Set set = new HashSet();
        System.out.println(set.add("jact"));
        System.out.println(set.add("jact"));//加入不了
        System.out.println(set.add(new cat("tom")));
        System.out.println(set.add(new cat("tom")));//没有问题  相当于使用匿名类生成对象,虽然内部属性相同,
        // 但由于没有类名所以成功
        //底层通过 equals 方法进行比较
        System.out.println("set="+set);

        //经典面试题
        System.out.println(set.add(new String("has")));
        System.out.println(set.add(new String("has")));//加入不成功
    }
}

@SuppressWarnings({"all"})
class cat{
    private String name;

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

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

接下来我们来看一个HashSet的练习,以及看看Set接口实现类不能重复的标准以及操作方法:

package jihe.Collection_.com;
//HashSet 底层源码    HashSet 练习
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;

@SuppressWarnings({"all"})
public class HashSetSoure {
    public static void main(String[] args) {
        Test1();
        Test2();
    }

    public static void Test1() {
    /* 定义一个 Employee 类 该类包含 private: name,age 要求:
        创建3个Employee对象 放入 HashSet中
        当name,age值相同时,认为员工相同,不能添加入HashSet集合中
         */
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("milan", 18));
        hashSet.add(new Employee("majunyi", 28));
        hashSet.add(new Employee("milan", 17));
        System.out.println("hashSet=" + hashSet);
    }

    public static void Test2(){
        /* 定义Employee_类,该类包括 private: neme,sal,birthday(MyDate)类,
        其中 birthday 为 MyDate类包括: year,month,day,要求:
         创建3个Employee对象 放入 HashSet中
         当name,birthday值相同时,认为员工相同,不能添加入HashSet集合中
         */
        HashSet set=new HashSet();
        set.add(new Employee_("majunyi",5000.67,new MyDate(2002,10,23)));
        set.add(new Employee_("zhangfei",40056,new MyDate(2003,9,21)));
        set.add(new Employee_("tom",6005.23,new MyDate(2001,7,17)));
        set.add(new Employee_("zhangfei",40056,new MyDate(2003,9,21)));
        set.add(new Employee_("shengjri",8004.32,new MyDate(2001,9,24)));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println("obj="+obj);
        }
    }
}

class Employee_{
    private String name;
    private double sal;//薪水
    private MyDate birthday;

    public Employee_(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee_)) return false;
        Employee_ employee_ = (Employee_) o;
        return Double.compare(employee_.sal, sal) == 0 && Objects.equals(name, employee_.name) && Objects.equals(birthday, employee_.birthday);
    }

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

    @Override
    public String toString() {
        return "Employee_{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}

class MyDate{
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

class Employee{
    private String name;
    private int age;

    public Employee(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 "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }


    //当name,age值相同时,认为员工相同,不能添加入HashSet集合中
    //那就必须在当 当name,age值相同时,返回相同的哈希值

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

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

}

从以上例子我们可以看出,我们可以通过重写类的hashCode方法和equals方法来实现对Set接口实现类不能重复的标准的控制及操作。

接下来我们通过这个例子来分析一下HashSet的子类LinkedHashSet的底层和应用场景:

package jihe.Collection_.com;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

//HashSet子类
//LinkedHashSet 底层为 LinkedHashMap  底层维护 数组+双向链表 存取元素顺序相同,但不能重复
@SuppressWarnings({"all"})
public class LinkedHashSet_ {
    public static void main(String[] args) {
        Test1();
        Test2();
    }
    public static void Test1(){
        //分析LinkedHashSet 底层
        Set set=new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);//添加失败
        set.add(new Customer("刘",1001));
        set.add(123);
        set.add("hsp");

        System.out.println("set="+set);//加入顺序和取出顺序相同
    }

    public static void Test2(){
        //创建Car 类,包含name ,price 要求:
        //当name 和 price 相同时不能添加
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥迪",300000));
        linkedHashSet.add(new Car("法拉利",1000000));
        linkedHashSet.add(new Car("奥拓",10000));
        linkedHashSet.add(new Car("奥迪",300000));
        linkedHashSet.add(new Car("保时捷",70000000));
        linkedHashSet.add(new Car("奥拓",10000));
        Iterator iterator = linkedHashSet.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("next="+next);
        }
    }
}

class Car{
    private String neme;
    private double price;//价格

    public Car(String neme, double price) {
        this.neme = neme;
        this.price = price;
    }

    public String getNeme() {
        return neme;
    }

    public void setNeme(String neme) {
        this.neme = neme;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "neme='" + neme + '\'' +
                ", price=" + price +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(neme, car.neme);
    }

    @Override
    public int hashCode() {
        return Objects.hash(neme, price);
    }
}

class Customer{
    private String name;
    private int no;

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

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

    public String getName() {
        return name;
    }

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }
}

下面我来为大家介绍TreeSet类,其中我为大家展示了他的排序规则以及相关源码的触发:

package jihe.Collection_.com;

import java.util.Comparator;
import java.util.TreeSet;

// 源码解读  可排序
// TreeSet 底层是 TreeMap
@SuppressWarnings({"all"})

public class TreeSet_ {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
               // 下面  调用String 的comparatTo 比较字符串大小(与C语言中字符串比较strcmp()函数效果相同)即按照字典排序  从小到大
                // 1. 若要从大到小 则交换下面的o1和o2
                return ((String) o1).compareTo((String) o2);

                // 2. 如果按照字符串长度大小排序
                //return ((String) o1).length()-((String) o2).length();// 但是认为长度相同的字符串就是相同的元素
                // 即认为 tom 和 jac 是相同的元素 ,不建议使用
            }
        });
        // 添加数据
        treeSet.add("majunyi");
        treeSet.add("风煞");
        treeSet.add("zhangyi");
        treeSet.add("tom");
        treeSet.add("tom");
        System.out.println(treeSet);

        /*  1. 当使用无参构造器创建 Treeset 对象时,仍然是无序的
         2. 希望按照添加元素,按照字符串大小来排序
         3. 那就需要使用TreeSet 的一个构造器,可以传入一个比较器(匿名内部类) 并指定排序方式
       TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return 0;
            }
        });
        构造器将传入的比较对象赋值给 TreeMap 的属性this.comparator
        public TreeMap(Comparator<? super K> comparator) {
                this.comparator = comparator;
             }

             在 调用 treeSet.add("")时,底层执行
              if (cpr != null) {//cpr 就是我们的匿名内部类
            do {
                parent = t;
               // 动态绑定到 匿名内部类(对象)compare
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else {// 如果相等,即返回0,这个 Key就没有加入
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
         */
    }
}

另外 让我们来看一道TreeSet的相关练习:

定义一个学生类(Student),它包含private成员变量name(姓名),id(学号),age(年龄)。每个属性都要写get,set方法,并重写 toString 方法输出 name, id,age,balance

要求:

创建Student类的 10个对象,并把这些对象放入 TreeSet 集合中(成员变量自定义)

按下面的要求对集合中的元素进行排序,并遍历输出:

  • 按年龄从小到大排序

  • 当年龄相同时,按学号从大到小排序

package jihe.zuoye;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

@SuppressWarnings({"all"})
public class Tree_Set {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (((Student) o1).getAge() != ((Student) o2).getAge()) {
                    return ((Student) o1).getAge() - ((Student) o2).getAge();
                } else
                    return ((Student) o2).getId() - ((Student) o1).getId();
            }
        });

        treeSet.add(new Student("tom", 202101, 15));
        treeSet.add(new Student("jact", 2021021, 16));
        treeSet.add(new Student("zhangfei", 202203, 17));
        treeSet.add(new Student("风煞", 2022012, 18));
        treeSet.add(new Student("smith", 20221025, 16));
        treeSet.add(new Student("bhfdhjs", 2021045, 19));
        treeSet.add(new Student("dfdjst", 202109, 14));
        treeSet.add(new Student("nfhafrojiti", 2000156, 19));
        treeSet.add(new Student("王刚", 2002031, 17));
        treeSet.add(new Student("dfreejwgru", 2003051, 18));

        Iterator iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println("Student=" + next);
        }
    }
}

class Student {
    private String name;
    private int id;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

运行效果展示:

 接下来就到了我们的双列集合Map,同样让我们来看看Map接口实现类的类图关系吧:

Map存储的是Key-Value键值对,其底层机制为 Node类,在上面HashSet的底层介绍中有讲到,那么我们还是再来对HashMap 的底层解读一边吧:

package jihe.Map_.com;


// HashMap 的底层为 数组+单向链表+红黑树
// 扩容机制和 HashSet 完全相同 底层为一个 tabe 数组 <K,V> 实际是 HashMap$Node 类,而且他还实现了 entry 接口

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

// 模拟 HashMap  扩容树化
@SuppressWarnings({"all"})
public class HashMap_ {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        for (int i=0;i<=12;i++){
            map.put(new A(i),"majunyi"); //Value 可以重复
        }
        
        //12个Key->Value,因为 虽然hashCode 都为100,但没有重写equals 方法,所以有12个对象
        Set set = map.entrySet();
        for (Object o :set) {
            Map.Entry entry=(Map.Entry) o;
            System.out.println(entry);
        }

    }
}


class A{
    private int num;

    public A(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
    // 所有 的A对象 hashCode 都为100;
    @Override
    public int hashCode() {
        return 100;
    }

    @Override
    public String toString() {
        return "A{" +
                "num=" + num +
                '}';
    }
}

可能大家看不懂上面的entry遍历HashMap的实现,没关系,接下来我将会为大家详细介绍,以及为大家展示Map接口类的六大遍历方法:

package jihe.Map_.com;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class HashMapTest {
    public static void main(String[] args) {
        Map map=new HashMap();
        /* 创建 Preson 类,包括 name, slo, id, 要求 添加三个员工对象
        键 id
        值 员工对象
        用两种不同的遍历方法 输出工资大于等于18000的员工
         */
        map.put(20221001,new Preson("jact",40002.23,202221001));
        map.put(20221012,new Preson("tom",3000.45,202221012));
        map.put(20222023,new Preson("majunyi",20005.65,20222023));

        //获取 Value

        // map.keySet() 方法
        System.out.println("map.keySet()方法:");
        // 1.增强 for
        Set set = map.keySet();//得到的是 Key  通过 map.get(Key)=Value
        for (Object o :set) {
            Preson preson=(Preson) map.get(o);
            if(preson.getSlo()>=18000){
                System.out.println(preson);
            }
        }
        System.out.println("\n\n\n");
        // 2. 迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            Preson preson=(Preson) map.get(next);
            if(preson.getSlo()>=18000){
                System.out.println(preson);
            }
        }

        // map.entrySet() 方法
        System.out.println("\n\n\nmap.entrySet() 方法:");
        Set set1 = map.entrySet();//得到 Node 键值对
        // 1.增强 for
        for (Object o :set1) {
            Map.Entry entry=(Map.Entry) o;
            Preson preson=(Preson) entry.getValue();
            if(preson.getSlo()>=18000){
                System.out.println(preson);
            }
        }
        System.out.println("\n\n\n");
        // 2. 迭代器
        Iterator iterator1 = set1.iterator();
        while (iterator1.hasNext()) {
            Object next =  iterator1.next();
            Map.Entry entry=(Map.Entry) next;
            Preson preson=(Preson) entry.getValue();
            if(preson.getSlo()>=18000){
                System.out.println(preson);
            }
        }
    }
}

class Preson{
    private String name;
    private double slo;
    private long id;

    public Preson(String name, double slo, long id) {
        this.name = name;
        this.slo = slo;
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getSlo() {
        return slo;
    }

    public void setSlo(double slo) {
        this.slo = slo;
    }

    public long getId() {
        return id;
    }

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

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

另外还有 另一种遍历方式  Collection values = map.values(); 我没有为大家展示,因为他和Set set = map.keySet();//得到的是 Key,而且最重要的是他不能向下转型为Object 类和Set的实现类,因此不能通过  map.get(Key)=Value 来获取Value,因此不常用,我就不给大家展示了。

如果大家有兴趣那就请看下面这个例子,其中展示了Map接口实现类的六大遍历方式:

package jihe.Map_.com;

import java.util.*;

//Map 接口方法和六大遍历方式
// HashMap 键无序 ,Hashtabe 键无序, LinkedHashMap 键存取顺序有序相同
@SuppressWarnings({"all"})
public class MapTest {
    public static void main(String[] args) {
        Test2();
    }
    public static void Test0(){
        //Map 接口特点
        // 1. Map 和 Collection 并列,Map 实现用于保存具有映射关系的数据: Key-Value(双列元素)
        Map map = new HashMap();
        map.put("no1","风煞");
        map.put("no2","张无忌");
        map.put("no1","majunyi");//Map 中 Key 不能重复,若出现Key重复 则替换Value
        map.put("no3","张无忌");//Map 中Value可以重复
        map.put(null,null);//可以有空
        System.out.println("map="+map);//无序,HashMap 双向链表
        //输出: map={no2=张无忌, no1=majunyi, no3=张无忌}
        map.put(75,"tom");
        map.put("tom",46);
        System.out.println("map="+map);

        //Key 和 Value 之间存在单项一对一的关系,即通过Key 总能找到对应的Value
        //通过 get 方法传入Key ,会返回对应的 Value
        System.out.println(map.get("no1")+"\n\n\n");// 输出:majunyi

        /* 1. Key-Value 是放在 HashMap$Node里的 node = newNode(hash, key, value, null)
         2. K-V 为了方便程序员的遍历,还会 创建 EntrySet 集合,该集合存放元素的类型 Entry ,而一个Entry
        对象就有K,V Entry<Entry<K,V>> 即: transient Set<Map.Entry<K,V>> entrySet;
        3. entrySet 中,定义的类型是Map.Entry,但是实际上存放的是 HashMap$Node
        这是因为 static class Node<K, V> implements Map.Entry<K,V>
        4. 当把 HashMap$Node 对象存放到entrySet 方便我们的遍历,因为 Map.Entry 提供了重要方法
        K getKey(); V getValue();
         */

        Set set=map.entrySet();
        System.out.println(set.getClass());
        //为了从HashMap$Node 中取出K-V
        // 1. 首先做一个向下转型
        for (Object obj : set) {
            //System.out.println("obj="+obj);
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }

        Set set1 = map.keySet();
        System.out.println(set1.getClass());
        Collection values = map.values();
        System.out.println(values.getClass());
        // 由于 Map 没有迭代器 因此需要将Map 进行封装成为数组便于遍历
    }

    public static void Test1(){
        //Map 接口方法
        Map map=new HashMap();
        // 1. put:添加
        map.put("邓超","孙俪");
        map.put("王宝强","马蓉");
        map.put("宋茜","马蓉");
        map.put("刘玲博",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");
        System.out.println("map="+map);

        // 2. remove:根据键(Key)删除映射
        map.remove(null);
        System.out.println("map="+map);

        // 3. get:根据键获取
        System.out.println(map.get("鹿晗"));

        // 4. size: 获取元素个数
        System.out.println(map.size());

        // 5. isEmpty:判断是否为空
        System.out.println(map.isEmpty());

        // 6. clear:清空所有

        // 7. containsKey 和 containsValue  :查找
        System.out.println(map.containsKey("鹿晗"));
        System.out.println(map.containsValue("孙俪"));
    }

    public static void Test2(){
        //Map 六大遍历方法
        Map map=new HashMap();
        map.put("邓超","孙俪");
        map.put("王宝强","马蓉");
        map.put("王宝强","majnuyi");
        map.put("宋茜","马蓉");
        map.put("刘玲博",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");

        System.out.println("         第一组");
        // 第一组. 先取出所有的Key 再取出 每一个Key 所对应的Value
        // 1. 增强 for  map.keySet方法转化为 Set 类
        System.out.println("****第一种方式****");
        Set set = map.keySet();//得到的是 Key  通过 map.get(Key)=Value
        for (Object o :set) {
            System.out.println(o +"-"+map.get(o));
        }
        // 2. 迭代器  利用上一步转化的 Set 集合
        System.out.println("****第二种方式****");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next+"-"+map.get(next));
        }

        System.out.println("         第二组");
        // 第二组:
        Collection values = map.values();//无法反向取 Key 即 map.get(value) 会报错
        // 1.增强 for
        System.out.println("****第一种方式****");
        for (Object o :values) {
            System.out.println(o);
        }
        // 2. 迭代器
        System.out.println("****第二种方式****");
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object next =  iterator1.next();
            System.out.println("next="+next);
        }

        System.out.println("         第三组");
        // 第三组:通过 EntrySet 来获取 K-V 即 map.entrySet方法转化为Set类
        Set set1 = map.entrySet();
        // 1. 增强 for
        System.out.println("****第一种方式****");
        for (Object o :set1) {// 运行多态 都为 HashMap$Node类对象
           // System.out.println("o="+o);
            Map.Entry entry=(Map.Entry) o;
            //System.out.println("entry="+entry);
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }
        // 2. 迭代器
        System.out.println("****第二种方式****");
        Iterator iterator2 = set1.iterator();
        while (iterator2.hasNext()) {
            Object next =  iterator2.next();
            //System.out.println("next-"+next);
            Map.Entry entry=(Map.Entry) next;
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }
    }
}

下面我来为大家介绍一下Hashtabe 的扩容规则:

package jihe.Map_.com;


import java.util.Hashtable;

/*  Hashtabe 存放 K-V 键值对
  hashtabe 的键值 K-V 都不能为 null
  其他原则和HashMap 相同
  Hashtabe(线程安全)synchronize 使用方法 和HashMap(线程不安全) 一样
 */
@SuppressWarnings({"all"})
public class Hashtabe_ {
    public static void main(String[] args) {
        Hashtable hashtable = new Hashtable();
        // hashtable.put("majunyi",null); 异常
        hashtable.put("majunyi",100);
        hashtable.put("zhangfei",200);
        hashtable.put("hello1",467);
        hashtable.put("129",364);
        hashtable.put(756,"5689");
        hashtable.put("yiweiyi",345);
        hashtable.put("maying","liyu");
        hashtable.put("shijei","liwieyi");
        hashtable.put(1233,7878);


        // 简单说一下Hashtabe 的底层
        //  1. 底层有数组 Hashtabe$Entry[] 数组,初始化大小为 11
        // 2. 临界值 threshold 11*0.75=8;
        // 3. 扩容:按照自己的扩容机制来扩容,并不是两倍
        // 4. 执行方法   addEntry(hash, key, value, index); 添加K-V 封装到Entry
        // 5. 当 if (count >= threshold) 成立时,即当前值大于等于临界值时,就进行扩容
        // 5. 按照 int newCapacity = (oldCapacity << 1) + 1; 的值进行扩容

    }
}
 

下面来看看 properties 类及其作用:

package jihe.Map_.com;

/*  properties 继承自 Hashtabe 类并实现了Map接口,K-V
使用特点和 Hashtabe 类似
Properties 还可以从xxx.properties文件中,加载到Properties 类对象并进行读取和修改
说明: 工作后 xxx.properties 文件通常作为配置文件,这个知识点在IO 流例举
 */

import java.util.Properties;

// Properties 方法

@SuppressWarnings({"all"})

public class MapProperties_ {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.put("john",100);
        properties.put("majunyi",200);
        properties.put("zhamgyi","majunyi");
        // 1. put:添加 键值对(K-V)不能为空
        // properties.put("majunyi",null); 空指针异常
        System.out.println(properties); //输出 {majunyi=200, zhamgyi=majunyi, john=100}

        // 通过 Key 获取对应值   properties.get();
        System.out.println(properties.get("majunyi"));// 输出 200

        // 删除
        properties.remove("john");
        System.out.println(properties); //输出 {majunyi=200, zhamgyi=majunyi}

        // 修改
        properties.setProperty("majunyi","zhangyi");
        System.out.println(properties); // 输出 {majunyi=zhangyi, zhamgyi=majunyi}

    }
}

最后让我们来看看 TreeMap类,在上面介绍 TreeSet的时候也有说到,那我们来进行进一步了解吧:

package jihe.Map_.com;


import java.util.Comparator;
import java.util.TreeMap;

// K-V 键值对 和TreSet 相同可以排序使元素存取有序 针对的都是(标准) Key
@SuppressWarnings({"all"})
public class TreeMap_ {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
               // 按照字符串大小
                // 下面  调用String 的comparatTo 比较字符串大小 Key (与C语言中字符串比较strcmp()函数效果相同)即按照字典排序  从大到小
                // 1. 若要从大到小 则交换下面的o1和o2
                return ((String) o1).compareTo((String) o2);

                // 2. 如果按照字符串长度大小排序 从小到大
              //  return ((String) o1).length()-((String) o2).length();// 但是认为长度相同的字符串就是相同的元素
                // 即认为 (Key)tom 和(Key)jac 是相同的元素,不会发生Value替换,直接添加失败 ,不建议使用
            }
        });
        treeMap.put("jact","杰克");
        treeMap.put("tom","汤姆");
        treeMap.put("smith","史密斯");
        treeMap.put("majunyi","风煞");
      //  System.out.println(treeMap); // 输出:{jact=杰克, majunyi=风煞, smith=史密斯, tom=汤姆}
        // 可以发现 现在是无序的

        System.out.println(treeMap);
        /*
        1. 当使用无参构造器创建 Treeset 对象时,仍然是无序的
         2. 希望按照添加元素,按照字符串大小来排序
         3. 那就需要使用TreeMap 的一个构造器,可以传入一个比较器(匿名内部类) 并指定排序方式
       TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return 0;
            }
        });
        构造器将传入的比较对象赋值给 TreeMap 的属性this.comparator
        public TreeMap(Comparator<? super K> comparator) {
                this.comparator = comparator;
             }
         */

        /*
        2.1 put: 第一次添加,把K-V 封装到 Entry 对象,放入 root
        Entry<K,V> t = root;
        if (t == null) {
            addEntryToEmptyMap(key, value);
            return null;
        }

        2.2 以后添加
         Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {// 遍历所有的 Key,给Key找合适的位置
                parent = t;
                cmp = cpr.compare(key, t.key);// 动态绑定到我们的匿名内部类的compare
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else {// 如果遍历过程中,发现准备添加的Key 和当前已有的Key 相等,就不添加
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }

         */
    }
}
 

他的排序规则和TreeSet 一样只不过他的排序标准是参照Key 来实现的。

接下来就到了我们用来操作 List、Set、Map接口实现类的集合的 Collections 类啦,下面就让我们来看看他所具有的主要操作方法吧:

package jihe.Collenctions_;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/*
Collenctions 是一个操作 Set, List, Map 等集合的工具类
Collenctions 中提供了一系列静态对集合 进行排序,查询,和修改等操作
 */
@SuppressWarnings({"all"})

public class CollenctionsTest {
    public static void main(String[] args) {
       // 介绍 Collenction 的常用方法
        // 创建一个 ArrayList 集合,作为测试例子
        List list = new ArrayList();
        list.add("tom");
        list.add("majunyi");
        list.add("smith");
        list.add("king");
        list.add("风煞");
        System.out.println("list="+list);//输出 list=[tom, majunyi, smith, king, 风煞]


        // Collection 中的所有方法都为静态方法,因此不需要创建对象,可直接用类名引用
        // 1. reverse(List): 反转 List 中的元素顺序
        Collections.reverse(list);
        System.out.println("list="+list);//输出 list=[风煞, king, smith, majunyi, tom]

        // 2. shuffle(List): 对List 集合元素进行随机排序
        Collections.shuffle(list);
        System.out.println("list="+list);// list=[tom, majunyi, smith, king, 风煞]

        // 3. sort(List): 对List按照自然顺序排列
        Collections.sort(list);
        System.out.println("自然排序后 list="+list);// list=[king, majunyi, smith, tom, 风煞]
        // 我们希望自定义排序规则:
       // sort(List,Comparator):指定 Comparator 产生的顺序对list进行排序
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                // 下面  调用String 的comparatTo 比较字符串大小(与C语言中字符串比较strcmp()函数效果相同)即按照字典排序  从小到大
                // 1. 若要从大到小 则交换下面的o1和o2
             //   return ((String) o1).compareTo((String) o2);

                // 2. 如果按照字符串长度大小排序
                return ((String) o1).length()-((String) o2).length();// 但是认为长度相同的字符串就是相同的元素
                // 即认为 tom 和 jac 是相同的元素 ,不建议使用
            }
        });
        System.out.println("按照字符串长度大小排序:list="+list); //list=[风煞, tom, king, smith, majunyi]

        // 4. swap(List, int, int): 将指定 list 集合的 i 处元素和 j 处元素 进行交换
        Collections.swap(list,0,1);
        System.out.println("交换后list="+list);//交换后list=[tom, 风煞, king, smith, majunyi]
        // 不能越界使用

        // 5. Object max(Collection): 根据元素的自然排序,返回给定集合中的最大元素
        System.out.println(Collections.max(list));//风煞

        // 6. Object max(Collection,Comparator): 根据 Comparator 指定的顺序,返回集合中的最大元素
        System.out.println(Collections.max(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                // 下面  调用String 的comparatTo 比较字符串大小(与C语言中字符串比较strcmp()函数效果相同)即按照字典排序  从小到大
                // 若要从大到小 则交换下面的o1和o2
                return ((String) o1).compareTo((String) o2);
            }
        }));//风煞

        // 7.  Object max(Collection): 根据元素的自然排序,返回给定集合中的最小元素
        // 8. Object max(Collection): 根据元素的自然排序,返回给定集合中的最小元素

        // 9. int frequency(Collection,Object): 返回指定集合中指定元素的出现次数
        System.out.println("majunyi 出现的次数"+Collections.frequency(list,"majunyi"));// 1

        // 10. void copy(list dest, List src): 将src 中的内容复制到dest中  dest大小不能小与 src
        // 所以必须先对 dest 赋值
        ArrayList dest =new ArrayList();
        for (int i=0 ;i<list.size(); i++){
            dest.add("java");
        }
        //拷贝
        Collections.copy(dest,list);
        System.out.println("dest="+dest); //dest=[tom, 风煞, king, smith, majunyi]

        // 11. boolean replaceAll(List list,Object oldVal, Object newVal): 使用新值替换 List 中的所有旧值
        // 如果list 中有majnuyi 则替换为 tom
        Collections.replaceAll(list,"majunyi","tom");
        System.out.println("list="+list); //list=[tom, 风煞, king, smith, tom]

    }
}

完啦!谢谢看完的朋友,那就送大家一个彩蛋吧

小彩蛋 🥚


public class HashSetSoure {
    public static void main(String[] args) {
      System.out.println("  ::##                                      ##::  \n" +
              "::::::##                                  ##::::::\n" +
              "::::::##                ## : ##           ##::::::\n" +
              "::::::::##            ##::##::##        ##::::::::\n" +
              "::::::::##        ####::##::####        ##::::::::\n" +
              "::::::::::    ####::::::::::::::####    ::::::::::\n" +
              "::::::::::  ##::::::::::::::::::::::##  ::::::::::\n" +
              "::::::::::##@@::::::::::::::::::::::@@##::::::::::\n" +
              "::::::::##@@##@@::::::::::::::::::@@##@@##::::::::\n" +
              "::::::##@@######@@::::::::::::::@@######@@##::::::\n" +
              "  ##::##@@####  ##@@::::::::::@@##  ####@@##::##  \n" +
              "    ::##@@########@@::::::::::@@########@@##::    \n" +
              "    ####@@##  ####@@::######::@@####  ##@@####    \n" +
              "      ##::@@####@@::##########::@@####@@::##      \n" +
              "      ##::::@@@@::::::######::::::@@@@::::##      \n" +
              "        ::::::::::::::::::::::::::::::::::        \n" +
              "          ::@@@@@@@@@@@@@@@@@@@@@@@@@@::          \n" +
              "                  ##::@@@@@@::##                  \n" +
              "                ##::::##@@##::::##                \n" +
              "              ######::##@@##::######              \n" +
              "            ##::::::####@@####::::::##            \n" +
              "            ######::##@@@@@@##::######            \n" +
              "            ######::##@@@@@@##::######            \n" +
              "            ##::::::##########::::::##            \n" +
              "              ######          ######              ");
       
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

风煞

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值