Java 集合(List、Set、Map)

简单了解红黑树

在这里插入图片描述

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

Collection

在这里插入图片描述

Collection方法

注意contains(All),remove(All)这种,比较的是内容而不是地址(前提是你这个类重写了equals方法),所以有下图:
在这里插入图片描述
contains方法是用传入参数的equals方法与集合中的每个元素进行比较,也就是说,集合中的每个元素都要进入contains的参数的equals方法中比较,直到找到相同元素才停止,见下图:
在这里插入图片描述

  • equals:判断当前集合和形参元素是否相同(相同则返回True)
  • retain:差集
  • toArray:集合转数组
  • Arrays.asLIst(待转换数组):数组转集合
List<String> list = Arrays.asList(new String[]{"ha", "hei", "ho"});
System.out.println(list);

结果是:
[ha, hei, ho]

但是,如果你想这样:

List<int[]> list1 = Arrays.asList(new int[]{12, 34, 56});
System.out.println(list1);

结果:
[[I@10f87f48]

java会认为new int[]{12, 34, 56}是一个元素,如果想让java识别为多个元素,目前为止可以用如下方法:

List list1 = Arrays.asList(new Integer[]{12, 34, 56});
// 或 List list1 = Arrays.asList(12,34,56);
System.out.println(list1);

结果:
[12, 34, 56]
  • iterator:迭代器
    在这里插入图片描述
Iterator iterator = c2.iterator();
// 方式1
for (int i = 0; i < c2.size(); i++) {
   System.out.println(iterator.next());
}

// 方式 2
while (iterator.hasNext()) {
   System.out.println(iterator.next());
}

在这里插入图片描述

  • iterator的remove方法
    在这里插入图片描述
LIst(jdk1.2出现)接口及其实现类:ArrayList、LinkedList以及Vector

首先这三者都实现类List接口,因此都存储有序的、可重复的数据。

ArrayList (主要实现类 jdk1.2及之后)

线程不安全,效率高,底层使用Object[]存储。

我们之前用的数组一旦定义了大小就无法改变,而ArrayList这个玩意儿可以动态的扩容,下面来看一下它是如何扩容的:

注意: 其扩容机制在jdk7和jdk8稍有不同,先来看jdk7

  • 当List list = new ArrayList()之后,这时是调用了空参构造器,那么底层是创建了长度为10的Object类型数组,叫elementData
  • 当list.add(元素)时,如果elementData的容量不够了,默认情况扩容到原来容量的1.5倍,同时将原来数组中的元素拷贝到新的数组中

**建议:**如果我们能大致的估计出元素个数,那么可以用带参的构造器,省得他总扩容复制元素。

再来看看jdk8:

  • 当List list = new ArrayList()之后,这时是调用了空参构造器,那么底层会将Object[] elementData初始化为{},并没有创建长度
  • 当第一次调用add时,底层才会创建长度为10的Object数组

后续的操作与jdk7是相同的。

LinkedList(jdk1.2及之后)

底层使用双向链表,插入、删除效率比ArrayList高。

它的源码倒是没啥东西:

  • 里面没有ArrayList的Object数组,而是定义了Node类型的first和last指针,默认值是null
    在这里插入图片描述
Vector(jdk1.0, 比较古老不常用)

线程安全,效率低,底层使用Object[]存储。

它的源码没咋改动过,通过Vector()构造器创建对象时,在底层创建了长度为10的数组,另外在扩容时,默认扩为原来数组的2倍。

List是有序的,因此一些操作不能在Collection中定义。
在这里插入图片描述
注意这个remove,它和Collection里那个remove的区别,后者参数是Object类型的,而这里的remove参数是int类型的,此外,ArrayList有两个remove方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一道List的面试题

说出结果:

在这里插入图片描述
这道题考察的是remove的两个方法:remove(int)和remove(Object),add方法传入参数只能是Object,而remove传一个int参数不会自动装箱(因为人家就有int的那个方法),所以这个remove(2)删除的是索引,结果为12;如果你想删内容,那么要这样:remove(new Integer(2));

https://www.bilibili.com/video/BV1Kb411W75N?p=534

Set

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
我们发现使用add方法时,如果是添加多个相同的元素,如Integer、String这种,则只会保存一个。但添加有些元素,比如我们自己定义了一个类时(若不指定hashCode方法),可以添加多个在equals看来相等的元素:

public class HashSetTest {
    public static void main(String[] args) {
        Set set = new HashSet();
        set.add(1);
        set.add(2);
        set.add("3");
        set.add("3");
        set.add(1);
        set.add(new Person("liu", 20));
        set.add(new Person("liu", 20));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

class Person {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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 &&
                name.equals(person.name);
    }

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

结果:
1
2
3
Person{name='liu', age=20}
10
Person{name='liu', age=20}

我们来分析一下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LinkedHashSet

我们把上述代码改成这样:

public class LinkedHashSet {
    public static void main(String[] args) {
        Set set = new java.util.LinkedHashSet();
        set.add(10);
        set.add(2);
        set.add("3");
        set.add("3");
        set.add(1);
        set.add(new Person1("liu", 20));
        set.add(new Person1("liu", 20));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

class Person1 {
    private String name;
    private int age;

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

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

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

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

只说把HashSet改成了LinkedHashSet,但结果变得有序了:
在这里插入图片描述
但这并不是说LinkedHashSet就是有序的,它是HashSet的子类。
在这里插入图片描述

TreeSet

这个集合可以给输入的数据排序,但你得实现比较器。也就是说在TreeSet中,判断两元素是否相等不再使用equals而是使用比较器。

我们还是用之前自定义的类:

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        treeSet.add(new Person2("All", 20));
        treeSet.add(new Person2("Peter", 17));
        treeSet.add(new Person2("Bob", 36));
        treeSet.add(new Person2("Emi", 55));

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

结果:
class treeset.java.Person2 cannot be cast to class java.lang.Comparable

因为我们自定义的类没有实现比较器,下面来实现:

class Person2 implements Comparable{
    private String name;
    private int age;

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

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        treeset.java.Person2 person = (treeset.java.Person2) o;
        return age == person.age &&
                name.equals(person.name);
    }

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

    @Override
    public int compareTo(Object o) {
        if (o instanceof Person2) {
            Person2 p = (Person2) o;
            return this.name.compareTo(p.name);
        }else {
            throw new RuntimeException("类型不匹配!");
        }
    }
}

结果:
Person{name='All', age=20}
Person{name='Bob', age=36}
Person{name='Emi', age=55}
Person{name='Peter', age=17}

但当传入两个同名的人而年龄不同时,只会保存一个,这种情况下如果你想把两个都保存就要写二级排序:

@Override
    public int compareTo(Object o) {
        if (o instanceof Person2) {
            Person2 p = (Person2) o;
            int com = this.name.compareTo(p.name);
            if (com != 0) {
                return com;
            }else {
                return Integer.compare(this.age, p.age);
            }

        }else {
            throw new RuntimeException("类型不匹配!");
        }
    }

我们当然也可以使用定制排序Comparator:

Comparator com = new Comparator(){

            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person2 && o2 instanceof Person2) {
                    Person2 p1 = (Person2) o1;
                    Person2 p2 = (Person2) o2;
                    return Integer.compare(p1.getAge(), p2.getAge());
                }else {
                    throw new RuntimeException("类型不匹配!");
                }
            }
        };

        TreeSet treeSet = new TreeSet(com);
        treeSet.add(new Person2("All", 20));
        treeSet.add(new Person2("Peter", 17));
        treeSet.add(new Person2("Bob", 36));
        treeSet.add(new Person2("Emi", 55));
        treeSet.add(new Person2("Bob", 55));

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

结果:
Person{name='Peter', age=17}
Person{name='All', age=20}
Person{name='Bob', age=36}
Person{name='Emi', age=55}
Set几道面试题

https://www.bilibili.com/video/BV1Kb411W75N?p=547

这题太骚了…

ArrayList 源码分析

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

LinkedList及源码分析

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

vector及源码

在这里插入图片描述

面试题 Collection List下三种结构的异同

在这里插入图片描述

Map

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

HashMap及底层原理

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

jdk7:
在这里插入图片描述

jdk8:
在这里插入图片描述
源码分析:

jdk7:
https://www.bilibili.com/video/BV1Kb411W75N?p=552

jdk8:
https://www.bilibili.com/video/BV1Kb411W75N?p=553

LinkedHashMap及底层原理

在这里插入图片描述
https://www.bilibili.com/video/BV1Kb411W75N?p=554

==其实HashSet和HashMap很像,new HashSet的时候也是用HashMap生成的,只不过把HashMap中的key作为HashSet中的值而已,至于value,设置成了static的Object对象,HashSet中所有的值的value都指向它(没啥意义)。

Map中的方法

在这里插入图片描述
在Map中没有迭代器了,那个只能在Collection中使用,但Map中的key是用set存储的,value是用collection存储的,键值对(entry/node)是用set存储的。因此只要能得到它们,想必也能访问Map了。

在这里插入图片描述

获取Map中的key:

public static void main(String[] args) {
        Map map = new HashMap();
        map.put(null, 5);
        map.put("AA", 6);
        map.put("Lu", 11);
        map.put("HH", 37.5);
        map.put("Pl", 0.44f);

        Set set = map.keySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
//        System.out.println(map);

    }

获取Map中的value:

Collection values = map.values();
for (Object obj : values) {
    System.out.println(obj);
}

遍历所有的key-value:

Set set1 = map.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()) {
    System.out.println(iterator1.next());
}

结果:
null=5
AA=6
HH=37.5
Lu=11
Pl=0.44

也可以做一个强制转换,分别得到key和value:

在这里插入图片描述
或者:

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

TreeMap

定制排序:

在这里插入图片描述
自然排序:
在这里插入图片描述
由于我们put的数据是User类型的,因此要我们自己实现CompareTo接口:

在这里插入图片描述

HashTable 及其子类 Properties

HashTable已经不怎么用了,关注一下它和HashMap的区别即可,类似Vector和ArrayList。

下面先简单了解一下Properties:

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

Collections工具类

这就和操作数组的工具类Arrays很相似了:

在这里插入图片描述
在这里插入图片描述
其中,使用copy的时候要注意目标List的size:

在这里插入图片描述
还有:

在这里插入图片描述
前面说过,ArrayList、HashSet和HashMap是线程不安全的,当你想使用线程安全时就把他们作为参数传到上面的方法中,得到的返回值就是线程安全的。

几道题目

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值