个人关于集合学习的笔记(超详细)

一、集合的概述

1.1、什么是集合?有什么用?

数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。

集合为什么说在开发中使用较多?

集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。

1.2、集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)

注意:list.add(100); //这样的情况为自动装箱

集合在java中本身是一个容器,是一个对象。

集合中任何时候存储的都是”引用”。

1.3、在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构? 数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:

数组、二又树、链表、哈希表...

以上这些都是常见的数据结构。

你往集合c1中放数据,可能是放到数组上了

你往集合。2中放数据,可能是放到二叉树上了。

所以说

你使用不同的集合等同于使用了不同的数据结构。

java中已经将数据结构实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择,哪一种合适的集合去使用即可。

new ArrayList();          //创建一个集合底层是数组
new LinkedList();        //创建一个集合对象底层是链表
new TreeSet();          //创建一个集合对象底层是二叉树

1.4、集合在java JDK中哪个包下?

java.util.*;

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

1.5、在java中集合分为两大类:

一类是单个方式存储元素:

单个方式存储元素,这一类集合中超级父接口:java.util.Collection;

一类是以键值对的方式存储元素:

以键值对的方式存储元素,这一类集合中超级父接口: java.util.Map;

二、关于Java.util.Collection接口中常用的方法

1、Collection中能存放什么元素?

没有使用”泛型“之前,Collection 中可以存储Object的所有子类型。

使用了“泛型“之后,Collection中只能存储某个具体的类型。

集合后会学习“泛型“语法。目前先不用管。Collection 中什么都能存,只要是object的子类型就行。(集合中不能直接存储基本数据类型,也不能存java对象,只是存储java对象的内存地址。)

2、Collection中的常用方法:

boolean add(Object e) 向集合中添加元素

int size() 获取集合中元素的个数

void clear() 清空集合

boolean contains(0bject o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false

boolean remove(Object o) 删除集合中的某个元素。

boolean isEmpty() 判断该集合中元素的个数是否为0

Object[ ] toArray() 调用这个方法可以把集合转换成数组。

2.1、boolean add(Object e) 向集合中添加元素

public class Collection01 {
    public static void main(String[] args) {
        //由于接口无法实例化,所以运用多态
        Collection c = new ArrayList();
        c.add(123);
        System.out.println(c);
    }
}

2.2、int size() 获取集合中元素的个数

public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("dad");
        c.add(7.12);
        System.out.println(c.size());   //3
    }
}

2.3、void clear() 清空集合

public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("dad");
        c.add(7.12);
        System.out.println(c.size());   //3
        c.clear();
        System.out.println(c.size());    //0
    }
}

2.4、boolean contains(0bject o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false

public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("java");
        c.add(7.12);
        boolean wj = c.contains("java");  
        System.out.println(wj);             //true
        boolean wj1 = c.contains("python");
        System.out.println(wj1);         //false
    }
}

2.5、boolean remove(Object o) 删除集合中的某个元素。

public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("java");
        c.add(7.12);
        boolean wj = c.contains("java"); //判断集合是否包含字符串“java”
        System.out.println(wj);   //此时有“java”字符串打印输出true
        c.remove("java");   //通过remove方法移除“java”字符串
        wj = c.contains("java");   //判断集合是否包含字符串“java”
        System.out.println(wj);  //此时集合中没有了java字符串输出结果为false
    }
}

2.6、boolean isEmpty() 判断该集合中元素的个数是否为0

public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("java");
        c.add(7.12);
        boolean b = c.isEmpty();     //这里判断字符串是否为空。结果为false
        System.out.println(b);
        c.clear();                 //清空集合
        b = c.isEmpty();          //再判断是否为空
        System.out.println(b);   //结果为true
    }
}

2.7、Object[ ] toArray() 调用这个方法可以把集合转换成数组。

 
public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add("java");
        c.add(7.12);
        Object[] obj = c.toArray();   //定义一个object类型的数组,然后将集合转换成的数组放到obj中
        for (int i = 0; i < obj.length; i++){  //进行遍历输出obj数组中所包含的元素查看效果
            System.out.println(obj[i]);   //123、java、7.12
        }
    }
}

三、总结所有的实现类

ArrayList:底层是数组。

LinkedList:底层是双向链表。

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

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

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

HashMap:底层是哈希表。

Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。

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

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

四、总结集合

List 集合存储元素的特点:

有序可重复

有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。

可重复:存进去 1,可以再存储一个 1。

Set (Map)集合存储元素的特点:

无序不可重复

无序:存进去的顺序和取出的顺序不一定相同。另外 Set 集合中元素没有下标。

不可重复:存进去1,不能再存储 1了。

SortedSet (SortedMap)集合存储元素特点:

首先是无序不可重复的,但是 SortedSet 集合中的元素是可排序的。

无序:存进去的顺序和取出的顺序不一定相同。另外 Set 集合中元素没有下标。

不可重复:存进去1,不能再存储 1了。

可排序:可以按照大小顺序排列。

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

五、关于集合遍历/迭代专题

下面将放上代码来进行演示迭代器的大致使用方法

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class Collection01 {
    public static void main(String[] args) {
        // 注意:以下讲解的遍历方式/迭代方式,是所有CoLLection通用的一种方式。
        // 在Map集合中不能用。在所有的CoLLection以及子类中使用。
        // 创建集合对象
        Collection c = new ArrayList(); // 后面的集合无所谓,主要是看前面的Collection接口,怎么遍历/迭代。
        //添加元素
        c.add(123);
        c.add(1.34);
        c.add("你好");
        c.add(new Object());
        // 集合Collection进行遍历/迭代
        // 第一步: 获取集合象的送代器Iterator
        Iterator it = c.iterator();
        // 第二步: 通过以上获取的迭代器对象开始迭代/遍历集合。
        /*以下两个方法是迭代器对象Iterator中的方法
        boolean hasNext()如果仍有元素可以迭代,则返回 true。
        Object next() 返回迭代的下一个元素。*/
        while(it.hasNext()){
            System.out.println(it.hasNext());
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}

如果new 的是HashSet对象,则输出的内容会是无序不可重复的

package Dome;
​
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
​
public class Collection01 {
    public static void main(String[] args) {
        // 创建HashSet集合对象:无序不可重复
        Collection c = new HashSet(); // 后面的集合无所谓,主要是看前面的Collection接口,怎么遍历/迭代。
        //无序:存进去的顺序和取出来的顺序不一定相同。
        //不可重复:存储了100,就不能再存储一个100
        c.add(123);
        c.add(777);
        c.add(100);
        c.add(6987);
        c.add(35435);
        c.add(100);
        //获取迭代器进行迭代
        Iterator it = c.iterator();
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj); /*100、777、123、6987、35435。可以看到添加了两个100最后只输出了一个100,验证了不可重复性。输出的顺序没有规律可循,则验证了无序性。
        }
    }
}

1、contains方法深入解析

boolean contains(Object o)

作用:判断集合中是否包含某个对象o,如果包含返回true ,如果不包含返回false。

contains方法是用来判断集合中是否包含某个元素的方法,那么在底层是怎么判断集合中是否包含某个元素的呢 ?

在底层contains方法调用了equals方法进行比对。equals方法返回true,就表示包含这个元素。

通过以下代码判断c集合中是否包含x?程序输出的结果为true还是false

import java.util.ArrayList;
import java.util.Collection;
​
public class Collection01 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList();
        //向集合中添加元素
        String s1 = new String("abc");
        c.add(s1);
        String s2 = new String("qwe");
        c.add(s2);
        //创建新对象String
        String x = new String("abc");
        //c集合中是否包含x?猜测一下结果是否为true还是false?
        System.out.println(c.contains(x));//判断集合中是否存在"abc"
        }
    }
​

结论: 存放在一个集合中的类型,一定要重写equals方法

注意:当集合的结果发生改变之后,迭代器必须重新获取。

2、集合元素的remove

重点:当集合的结构发生改变时,送代器必须重新获取,如果还是用以前老的迭代器,会出现

异常:java.util.ConcurrentModificationException

当我们使用 引用“ . ”remove方法删除了元素后,此时集合的结构发生了改变,会出现异常错误,需要重新获取迭代器。

出异常根本原因是:集合中元素删除了,但是没有更新送代器(送代器不知道集合变化了)

如果不重新获取迭代器,可以使用迭代器来进行删除。

送代器去删除时,会自动更新送代器,并且更新集合(删除集合中的元素)。

示例代码:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class Collection01 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add(123);
        c.add(435);
        c.add(1.23);
        Iterator it = c.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
            it.remove(); // 删除的一定是送代器指向的当前元素
        }
        System.out.println(c.size());
    }
}

总结:

重点:当集合的结构发生改变时,送代器必须重新获取,如果还是用以前老的送代器,会出现异常:java.util.ConcurrentModificationException 重点:在送代集合元素的过程中,不能调用集合对象的remove方法,删除元素:c.remove(o); 送代过程中不能这样。 会出现 :java.util.ConcurrentModificationException 重点:在送代元素的过程当中,一定要使用送代器Iterator的remove方法,删除元素不要使用集合自带的remove 方法删除元素。

3、List接口的常用方法

3.1、List集合存储元素特点:有序可重复

有序:List集合中的元素有下标。

从0开始,以1递增。

可重复:存储一个1,还可以再存储1。

3.2、List既然是Collection接口的子接口,那么肯定List接口有自己“特色”的方法:

以下只列出List接口特有的常用的方法:

在指定的索引位置插入一个元素:void add(int index, E element)

获取指定索引位置的元素 :E get(int index)

查找指定对象第一次出现的索引位置: int indexOf(Object o)

查找指定对象最后一次出现的索引位置:int lastIndexOf(Object o)

删除指定索引的元素:E remove(int index )

指定索引位置的元素替换为新的元素 :E set(int index, E element)

4、ArrayList集合

1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)

2、集合底层是一个object[]数组。

3、构造方法:

new ArrayList();

new ArrayList(20);

示例代码:

import java.util.ArrayList;
import java.util.List;
​
public class Collection01 {
    public static void main(String[] args) {
        // 默认初始化容量是10
        // 数组的长度是10
        List list = new ArrayList();
        //集合的size()方法是获取当前集合中元素的个数。不是获取集合的容量
        System.out.println(list.size()); // 0
​
        // 指定初始化容量
        // 数组的长度是20
        List list1 = new ArrayList(20);
        // 集合的size()方法是获取当前集合中元素的个数。不是获取集合的容量
        System.out.println(list1.size()); //0
    }
}

4、ArrayList集合的扩容:

增长到原容量的1.5倍。

ArrayList集合底层是数组,怎么优化?

尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集和的时候预估计元素的个数,给定一个初始化容量。

5、数组优点:

检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)

6、数组缺点:

随机增删元素效率比较低。

另外数组无法存储大数据量。(很难找到一块非常巨大的连续的内存空间。)

7、向数组末尾添加元素,效率很高,不受影响。

8、面试官经常问的一个问题 ?

这么多的集合中,你用哪个集合最多?

答:ArrayList集合。

9、ArrayList集合是非线程安全的。(不是线程安全的集合。)

HashSet集合转挽成List集合

10、ArrayList: 把检索发挥到极致。(未尾添加元素率还是高的)。

LinkedList: 把随机增删发挥到极致。

加元素都是往未尾添加,所以ArrayList用的比LinkedList多。

示例代码:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
​
public class Collection01 {
    public static void main(String[] args) {
        // 创建一个Hashset集合
        Collection c = new HashSet();
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(50);
        // 通过这个构造方法就可以HashSet集合转挽成List集合。
        List mylist2 = new ArrayList(c);
        for(int i = 0; i < mylist2.size(); i++){
            System.out.println(mylist2.get(i));
        }
    }
}

5、vector

01、底层也是一个数组。

02、初始化容量:10

03、怎么扩容的?

扩容之后是原容量的2倍,10-->20-->40--> 80

04、ArrayList集合扩容特点:

ArrayList集合扩容是原容量1.5倍

05、Vector中所有的方法都是线程同步的,都带有synchronized关健字是线程安全的。效率比较低,使用较少了

使用示例代码:

 
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
​
public class LinkList {
    public static void main(String[] args) {
        //创建一个Vector集合
        List vector = new Vector();
        //添加元素
        vector.add(123);
        vector.add(3.14);
        vector.add("你好");
        Iterator it = vector.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
​
    }
}

六、泛型

01、自JDK5之后就有了泛型机制

02、用泛型来指定集合中存储的数据类型。

03、在使用了泛型机制之后,表示某集合中只允许存储自定义的数据类型

04、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)

05、使用了泛型好处是什么?

第一:集合中存储的元素类型统一了。

第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!

06、泛型的缺点是什么?

导致集合中存储的元素缺乏多样性!

使用代码示例:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
​
public class MyList {
    public static void main(String[] args) {
        //指定List集合中只能存储Animal,那么存储string 就编译报错了。
        //这样用了泛型之后,集合中元素的数据类型更加统一了。
        List<Animal> list = new ArrayList<Animal>();
        Cat cat = new Cat();
        Bird bird = new Bird();
        list.add(cat);
        list.add(bird);
        //获取迭代器
        //表示这个迭代器中存储的是Animal类型的数据
        Iterator<Animal> it = list.iterator();
        while(it.hasNext()){
            // 使用泛型之后,每一次跌代返回的数据都是Animal类型
            Animal a = it.next();
            if (a instanceof Cat){
                Cat newcat = (Cat)a;
                newcat.Move();
            }
            if (a instanceof Bird){
                Bird newBird = (Bird)a;
                newBird.Sing();
            }
        }
    }
}
class Animal{
    public void Name(){
        System.out.println("这里是所有的动物");
    }
}
class Bird extends Animal{
    public void Sing(){
        System.out.println("鸟儿在歌唱");
    }
}
class Cat extends Animal{
    public void Move(){
        System.out.println("猫在走猫步");
    }
}

我们还可以自定义泛型:自定义泛型的时候,<>尖括号中的是一个标识符,随便写。java源代码中经常出现的是:

<E>和<T>

E是Element单词首字母。

T是Type单词首字母。

七、增强for循环( foreach)

语法格式:

for(元素类型 变量名 : 数组或集合){

System.out.println(变量名);

}

这种方式效率比较高,因为获取key和value 都是直接从node对象中获取的属性值

这种方式比较适合于大数据量。

foreach有一个缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。

使用代码示例:

 
public class MyList{
    public static void main(String[] args) {
        //创建一个String类型的数组
        String arr[] = {"123", "456", "789"};
        for(String s : arr){
            System.out.println(s);
        }
    }
}

八、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) 向Map集合中添加键值对

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(Object key) 通过key删除键值对

int size() 获Map集合中键值对的个数。

Collection<V> values() 获Map集合中所有的value,返回一个Collection

Set<Map.Entry<K,V>> entrySet() 将Map集合转换成Set集合

注意 : Map集合通过entrySet()方法转换成的这Set集合,Set集合中元素的类型是Map. Entry<K, V>

Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类

假设现在有一个Map集合,如下所示 :

keyvalue
1zhangsan
2lisi
3wangwu
4zhaoliu

01、V put(K key,V value) 向Map集合中添加键值对

示例代码:

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
    }
}

02、V get(Object key) 通过key获取value

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //通过key获取value
        String value2 = map.get(2);
        System.out.println(value2);
    }
}

03、int size() 获Map集合中键值对的个数。

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //获Map集合中键值对的个数。
        int Num = map.size();
        System.out.println(Num);  //4
    }
}

04、V remove(Object key) 通过key删除键值对

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //通过key删除键值对
        map.remove(2);
        //获取集合中键值对的个数
        System.out.println(map.size()); //3
    }
}

05、boolean containsKey(Object key) 判断Map中是否包含某个key

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //判断Map中是否包含某个key
        boolean b = map.containsKey(2);
        System.out.println(b); //true
    }
}

06、boolean containsValue(Object value) 判断Map中是否包含某个value

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //判断Map中是否包含某个Value
        boolean b = map.containsValue("zhangsan");
        System.out.println(b); //true
    }
}

07、Collection<V> values() 获Map集合中所有的value,返回一个Collection

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //获Map集合中所有的value,返回一个Collection
        Collection collection = map.values();
        for (Object obj : collection){
            System.out.print(obj);  //zhangsan,wangwu,lisi,zhaoliu
        }
    }
}

08、void clear() 清空Map集合

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //清空Map集合
        map.clear();
        System.out.println(map.size());  //0
    }
}

09、判断Map集合是否为空

import java.util.HashMap;
import java.util.Map;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //清空Map集合
        map.clear();
        //判断是否为空
        boolean b = map.isEmpty();
        System.out.println(b);  //true
    }
}

10、遍历Map集合的方法

第一种方式: 获取所有的key,通过遍历key,来遍历value

import java.util.*;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //获取所有的key,所有的key是一个Set集合
        Set<Integer> set = map.keySet();
        //遍历Set集合,获取每一个key值,通过key值来获取value
        //创建迭代器
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            Integer Num = it.next();
            String obj = map.get(Num);
            System.out.println(Num + " " + obj);
        }
    }
}

第二种方法: Set<Map.Entry<K,V>> entrySet()

以上这个方法是把Map集合直接全部转换成Set集合。Set集合中元素的类型是 : Map.Entry

import java.util.*;
​
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"wangwu");
        map.put(3,"lisi");
        map.put(4,"zhaoliu");
        //把Map集合直接全部转换成Set集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //此时遍历Set集合,就相当于遍历了Map集合
        for(Map.Entry<Integer,String> Node : set){
            System.out.println(Node);
        }
    }
}

九、HashMap集合:

1、HashMap底层是哈希表/散列表的数据结构。

底层实际上是一个数组(一维数组)。

哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)

2、哈希表是一个怎样的数据结构呢 ?

哈希表是一个数组和单向链表的结合体。

数组:在查询方面效率很高,随机增删方面效率很低。

单向链表:在随机增删方面效率较高,在查询方面效率很低。

哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

3、HashMap集合的key部分特点:

无序,不可重复。

为什么无序?因为不一定挂到哪个单向链表上。

不可重复是怎么保证的??equals方法来保证HashMap集合的key不可重复

如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放到HashSet集合中了。

所以HashSet集合中的元嘉也需要同时重写hashCode()和equals()方法

4、哈希表HashMap 使用不当时无法发挥性能!

假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们称为:散列分布不均匀。

什么是散列分布均匀?

散列分布均与需要你重写hashCode()方法时有一定的技巧。

5、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写 hashCode和equals方法。

6、HashMap集合的默认初始化容量是16,默认加载因子是0.75。这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

重点:记住: HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

7、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!equals方法有可能调用,也有可能不调用。

拿put(k,v)举例,什么时候equals不会调用?

k.hashCode()方法返回哈希值,

哈希值经过哈希算法转换成数组下标。

数组下标位置上如果是null,equals不需要执行。

拿get(k)举例,什么时候equals不会调用?

k.hashCode()方法返回哈希值,

哈希值经过哈希算法转换成数组下标。

数组下标位置上如果是null,equals不需要执行。

8、 如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。

9、hashCode()方法和equals()方法,这两个方法需要同时生成。

10、终极结论:放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重与hashCode方法和equals方法。

11、对于哈希表数据结构来说 :

如果o1利o2的hash值相同,一定是放到同一个单向链表上。

当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”

十、Hashtable集合

HashMap集合的key value 都是可以为null的。

Hashtable的key 可以为null吗?

Hashtable的key和value都是不能为null的。

HashMap集合的key和value 都是可以为null的。

Hashtable方法都带有synchronized : 线程安全的。线程安全有其它的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。

Hashtable和HashMap 一样,底层都是哈希表数据结构。Hashtable的初始化容量是11,默认加载因子是:0.75f Hashtable的扩容是:原容量* 2 + 1

十一、Properties

目前只需要掌properties属性类对象的相关方法即可。

Properties是一个Map集合,继Hashtable,Properties的key和value都是String类型。

Properties被称为属性类对象。

Properties是线程安全的。

关于Properties的方法:

setProperty() 存入字符串

getProperty() 取出字符串

示例代码:

 
import java.util.Properties;
​
public class PropertiesTest {
    public static void main(String[] args) {
        //创建Properties对象
        Properties properties = new Properties();
        //向Properties中存入数据
        properties.setProperty("url", "fiushdf");
        properties.setProperty("driver", "mybitis");
        properties.setProperty("username", "data");
        properties.setProperty("password", "123456");
        //在Properties中取出数据(通过key来获取value)
       String url =  properties.getProperty("url");
       String driver =  properties.getProperty("driver");
       String username =  properties.getProperty("username");
       String password =  properties.getProperty("password");
        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);
    }
}

十二、TreeSet集合

1、TreeSet集合底层实际上是一个TreeMap。

2、TreeMap集合底层是一个二叉树。

3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。

4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序称为:可排序集合。

代码示例:(自定义类如何进行排序)

第一种方式:

package homework;
​
​
import java.util.TreeSet;
​
public class TreeSetTest {
    public static void main(String[] args) {
        //创建一个TreeSet对象
        TreeSet<Vip> vips = new TreeSet<>();
        //向vips中添加对象
        vips.add(new Vip("zhangsan",12));
        vips.add(new Vip("lisi",79));
        vips.add(new Vip("wangwu",79));
        vips.add(new Vip("bgwu",56));
        //遍历vips集合
        for (Vip vip : vips){
            System.out.println(vip);
        }
    }
}
class Vip implements Comparable<Vip>{
    String name;
    int age;
​
    public Vip(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Vip{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    @Override
    public int compareTo(Vip o) {
        if (o.age == this.age){
            //年龄相同时按名字排序
            //姓名是String类型可以直接比。调用compareTo方法来完成。
            return this.name.compareTo(o.name);
        }else{
            return this.age - o.age;
        }
    }
}

第二种方式:

package homework;


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

public class TreeSetTest {
    public static void main(String[] args) {
        //创建一个TreeSet对象
        //使用匿名内部类的方式(这个类没有名字。直接new接口)给构造方法传递一个比较器。
        TreeSet<Vip> vips = new TreeSet<>(new Comparator<Vip>() {
            @Override
            public int compare(Vip o1, Vip o2) {
                if (o1.age == o2.age){
                    return o1.name.compareTo(o2.name);
                }else {
                    return o1.age - o2.age;
                }
            }
        });
        //向vips中添加对象
        vips.add(new Vip("zhangsan",12));
        vips.add(new Vip("lisi",79));
        vips.add(new Vip("wangwu",79));
        vips.add(new Vip("bgwu",56));
        //遍历vips集合
        for (Vip vip : vips){
            System.out.println(vip);
        }
    }
}
class Vip{
    String name;
    int age;

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

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

二叉树的原理:

1、自平衡二叉树。遵循左小右大原则存放

2、遍历二叉树的时候有三种方式:

前序遍历:根左右 中序遍历:左根右 后序遍历:左右根

注意:

前中后说的是“根”的位置。根在前面是前序,根在中间是中序,根在后面是后序。

3、TreeSet集合/TreeMap集合采用的是:中序遍历方式。Iterator迭代器采用的是中序遍历方式,左根右

最终的结论 :

放到TreeSet或者TreeMap集合key部分的元素要想做到排序包括两种方式:

第一种: 放在集合中的元素实现java.lang.Comparable接口。

第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。

Comparable利Comparator怎么选择呢?

当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。

如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。

Comparator接口的设计符合OCP原则。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值