Java进阶--集合

声明

  • 学习笔记
  • 仅供学习和参考

集合

一 集合概述

1 什么是集合

​ 字面意思:集合就是把具有相同性质的一类东西汇聚成一个整体。在面向对象中,对事物都是以对象的形式体现。为了方便对多个对象的操作,需要存储多个对象的容器,而集合就是存储多个对象最常用的一种方式。

2 集合的主要操作

  1. 将对象添加到集合中
  2. 从集合中删除对象
  3. 从集合中查找对象
  4. 从集合中修改对象

3 集合与数组的区别

​ 在不确定需要多少个对象时,若使用数组存储,数组长度不好定义;但是集合解决了这个问题。在集合中可以存储任意类型的对象,且集合大小是可变的。

数组集合
容器
大小一旦创建,大小固定大小一直可变
类型基本类型和引用类型引用类型
数据存储的数据类型相同存储任意类型

4 集合框架

​ 从各种集合形式中提取公共要素,形成一个集合的解决方案,就是Java的集合框架。Collection包括其子接口的超级接口是Iterable,Collection和Map是并列关系。

  • 单列集合
Collection(接口)
List(子接口)Set(子接口)
ArrayList(实现类)LinkedList(实现类)Vector(实现类)HashSet(实现类)TreeSet(实现类)
LinkedHastSet(实现类)
  • 双列集合
Map(接口)
Hashtable(实现类)HashMap(实现类)TreeMap(实现类)
Properties(实现类)LinkedHashMap(实现类)
(1) Java集合框架的接口
接口特点
Collection集合的根接口,没有实现类,用于引用子接口的对象。Collection 是一组对象的集合。
List元素之间是有序的,元素可以重复。通过索引类访问元素,允许在指定位置插入元素。
Set元素之间无序的,元素不可重复。
Map存储 Key/Value对 的集合。key 值不允许重复,每个 可以最多只能映射一个值。Map 是一组 Key/Value对 的集合。
(2) Java集合框架的实现类
接口实现类特点
ArrayList底层使用数组实现,长度默认为0。如果集合元素的个数超出当前数组大小,则创建新的扩容60%的数组,并将原来的数组拷贝过来。由于数组是按内存位置依次存放,因此查找快,但是增删元素涉及到后面的元素移动,因此增删元素要慢。
ListLinkedList底层使用链表实现。增删元素时,链表只需要增加或删除一个节点,这样的增删效率较高,但查询时需要一个个的遍历,所以查询效率较低。
Vector底层使用数组实现。Vector 是线程安全的,所以效率略低。ArrayList 是线程不安全的所以效率较高
SetHashSet底层是以哈希表实现的,所有元素是按hashCode 的值存放。HashSet是线程不安全,存取速度快。
TreeSet底层使用红-黑树的数据结构实现。要求元素实现Comparable接口,默认对元素进行自然排序(String)。
LinkedHashSet底层使用链表技术实现,但是结合了哈希值实现存储。LinkedHashSet的增删效率高,查询效率低。
HashMap底层是哈希表存储。HashMap 没有对 key 排序,可以存入null键和null值。HashMap 是线程不安全的。
MapTreeMap底层是红-黑树的数据结构实现。TreeMap 对 key 进行排序,要求 key 实现 Compareable 接口。
HashTable底层是哈希表数据结构。HashTable 是线程安全的,不可以存入null键和null值。HashTable 效率较低,已被HashMap代替。
LinkedHashMap底层使用链表技术实现,但是结合了哈希值实现存储。LinkedHashMap 的增删效率高,插叙效率低。
(3) 应用场景

​ 出现这么多集合容器的原因:因为每一个容器对数据的存储方式不同,为各种存储方式提供了多样化的选择(存储+操作)。

接口描述
Collection这是一个纯粹的接口,用于引用 List和Set 的实现对象。
LIst如果需要保留存储顺序,并且允许保留重复数据,使用 LIst 接口的实现。如果查询较多,那么使用 ArrayList。如果存取较多,那么使用 LInkedList。如果需要线程安全,那么使用Vector。
Set如果不需要保留存储顺序,并且需要去掉重复元素,使用 Set 接口的实现类。如果需要将元素排序,那么使用TreeSet。如果不需要排序,使用 HashSet。HastSet 比 TreeSet 效率高。如果需要保留存储顺序,又要过滤重复元素,那么使用 LinkedHashSet。
Map如果需要保留 key/value 形式数据,使用 Map 接口的实现类。如果需要将元素排序,那么使用 TreeMap。如果不需要排序,使用HashTable。HastTable 比 TreeMap 效率高。

二 Collection

1 什么是Collention

​ Colletion 是一个集合接口,为集合对象提供了最大化的同一操作方式。集合类都是在 java.util 包中,因此 Collection 接口就是 java.util.Collection。Collection 没有实现类,通常用于对实现子接口的对象引用。例如:使用 List 的具体实现类 ArrayList 创建一个集合对象:

Collection collection = new ArrayList();// 向上转型

2 基本操作

方法声明方法作用
add(E e)向集合添加元素,参数类型是 Object。成功返回 true,失败返回 false。
remove(Object o)删除指定元素。成功返回 true,失败返回 false。
size()获取集合中元素的个数
clear()清空集合中的所有元素
isEmpty()判断集合是否为空

3 元素包含

方法声明方法作用
contains(Objec o)判断集合中是否包含指定对象

注意:

​ contains 内部是依赖于 equals 方法进行比较的,所有支持 contains 判断的对象必须重写 equals 方法。请注意 Java 规范:重写 equals 方法时重写 hashCode 方法。例如:Person类 支持 contains 操作,就需要重写 equals 方法。

import java.util.Objects;

public class Person {
    private int id;
    private String name;

    public Person() {
    }

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

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

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

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

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

public class CollectionContainsDemo {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Person(1,"李"));
        collection.add(new Person(2,"王"));
        collection.add(new Person(3,"张"));
        System.out.println(collection.contains(new Person(1,"李")));
        System.out.println(collection.contains(new Person(3,"zhang")));

    }
}

4 集合操作

(1)集合关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVNCuUe9-1676430750905)(G:\Java-栈\课程笔记\集合\集合.PNG)]

​ 假定上面有 三个集合:A ,B和C,其中A的范围1和3,B的范围3和4,C的范围2。

​ 现在有以下关系:

​ 子集:集合C 是集合A的子集,因为C的所元素都在A中存在。

​ 并集:集合A和集合B的并集,范围是1,3,4,即二者的所有的元素。

​ 交集:集合A和集合B的交际,范围是3,即二者共有的所有元素。

​ 补集:集合B相对于集合A的补集,范围是4,即在集合B中,除去集合A和B的交集,剩下的所有元素称为B对A的补集。

(2)集合实现
方法集合关系方法描述equals用否
c.addAll(Collection c1)并集把c1的元素添加到c集合中去
c.removeAll(Collection c1)补集删除c集合中与c1的交集元素
c.retain(Collection c1)交集保留c集合与c1的交集元素,其他的元素一并删除
c.containsAll(Collection<?> c1)子集判断集合中是否包含指定集合。和 contains 方法一样,也是使用equals 方法判断两个对象是否相等
public class CollectionContainsDemo {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Person(1,"李"));
        collection.add(new Person(2,"王"));
        collection.add(new Person(3,"张"));
        
        Collection collection1 = new ArrayList();
        collection1.add(new Person("李"));
        collection1.add(new Person("王"));
        collection1.add(new Person("zhang"));
        System.out.println("-----------并集");
        System.out.println(collection.addAll(collection1));
        System.out.println("并集:"+collection);
        System.out.println("-----------补集");
        System.out.println(collection.remove(collection1));
        System.out.println("补集:"+collection);
        System.out.println("-----------交集");
        System.out.println(collection.retainAll(collection1));
        System.out.println("交集:"+collection);
        System.out.println("------------子集");
        System.out.println(collection.contains(collection1));

    }
}
5 集合转数组
方法声明方法描述
Object[] toArray()集合转成数组。只能使用 Object[] 作为返回类型
toArray(T[])集合转成数组。事先指定数组的类型,可以避免强制转型
public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Person(1,"李"));
        collection.add(new Person(2,"王"));
        collection.add(new Person(3,"张"));
        Object[] objects = collection.toArray();
        System.out.println(Arrays.toString(objects));
        Person[] people = new Person[collection.size()];
        Object[] objects1 = collection.toArray(people);
        System.out.println(Arrays.toString(objects1));
    }
}
// 结果
[Person{id=1, name='李'}, Person{id=2, name='王'}, Person{id=3, name='张'}]
[Person{id=1, name='李'}, Person{id=2, name='王'}, Person{id=3, name='张'}]

Process finished with exit code 0
6 迭代器

​ Java 实现了迭代器 Iterator 对象,它提供一些方法实现了对集合中元素的遍历。Collection 接口定义了获取迭代器的方法(iterator()),所有的Collection类型的集合都可以通过这个方法获取去自身的迭代器。

(1)Iterator< E > 接口

​ 该接口就是迭代器接口,定义了常见的迭代方法:

方法声明方法描述
boolean hashNext()判断集合中是否有下一个元素,如果有元素就返回true,没有返回false。
E next()返回集合中的下一个元素,注意:如果没有下一个元素时,调用 next 元素会抛出 NoSuchElementException
void remove()从集合中移除迭代器返回的最后一个元素(可选操作)

注意:

​ 每个迭代器中都有一个隐含的位置指针,从集合中获得迭代器时,这个位置执政只想第一个元素之前,此时直接获取元素时将抛出异常,必须调用 next() 将位置指针移到第一个元素的位置上才可以正确的访问。

最开始指针指在第一行之前,迭代前要调用next()将指针移到下一行(或者也叫游标)
0
1
2
3
4
(2)迭代器遍历相关操作
while 循环
public class Demo04 {
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");

        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println(next);
        }
    }
}
// 结果
张三
李四
王五

Process finished with exit code 0
for 循环

​ 一般不使用这种形式

public class Demo05 {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<String>();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");

        for ( Iterator<String> iterator = collection.iterator();iterator.hasNext();) {
            String next = iterator.next();
            System.out.println(next);
        }
    }
}
// 结果
张三
李四
王五

Process finished with exit code 0
使用迭代器清空集合

​ 一般不使用这种操作方式

public class Demo06 {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<String>();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");

        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();// 删除当前迭代的元素
        }
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next);
        }
    }
}
// 结果

Process finished with exit code 0

注意:

  1. ​ 如果迭代器已经只想集合的末尾,如果在调用 next()返回 NoSuchElementException异常 。
  2. 在调用remove之前没有调用next是不合法的,会抛出 IllegalStateException 异常。
不安全操作

​ 在对集合进行迭代过程中,不允许出现元素的非迭代器操作,因为这样会产生安全隐患。在集合遍历中,使用迭代器操作集合对象时,又使用结合其他 API 操作集合时,将会抛出并发修改异常(ConcurrentModificationException)。

public class Demo06 {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<String>();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");

        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            iterator.next();
            iterator.remove();
            collection.add("a");
        }
    }
}
// 结果
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at com.etime04.Demo06.main(Demo06.java:16)

Process finished with exit code 1

三 List

1 什么是List

​ List 是一个有序的集合,它按元素加入到集合中的顺序依次摆放。注意:不是按集合中对象内的属性值排序,这里是按插入位置的顺序。

0123456位置

​ List的特点:

  1. 元素有序
  2. 元素有下标
  3. 元素可重复
  4. 遍历时存入顺序和取出顺序一致

2 特有方法

​ 由于List集合中元素是有序的,它的所有实现类都支持索引,这些方法都是和索引相关。

操作方法声明方法描述
Boolean addAll(int index,Collection c)指定位置添元素
增加void add(int index,E element)指定位置添元素
删除E remove(int index)删除指定位置元素
修改E set(int index,E element)返回的是需要替换的集合中的元素
E get(int index)注意:IndexOutBoundsExcption
int indexOf(Object 0)找不到返回-1
int lastIndexOf(Object o)找不到返回-1
获取List subList(int fromIndex,int toIndex)求子集和:[fromIndex,toIndex)
迭代listIterator()

注意:

​ 在JavaSE中 List 名称的类型有两个,一个是 java.util.List 集合接口,一个是 java.awt.List 图形界面的组件,别导错包。

(1)常用功能
import java.util.ArrayList;
import java.util.List;

public class Demo04 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();// 接口的多态 向上转型 父接口与子接口
        list.add("li");
        list.add("zhang");
        list.add("wang");
        System.out.println(list);
        list.add(1,"zhao");// 指定位置 添加元素
        System.out.println(list);

        System.out.println("---------------------");
        List<String> list1 = new ArrayList<>();
        list1.add("小李");
        list1.add("小张");
        list1.add("小王");
        list.addAll(1,list1);
        System.out.println(list);// 把list1的元素添加到 list中下标为1(包括1)的指定位置

        System.out.println("---------------------");
        // 根据索引获取值获取元素
        System.out.println("get:"+list.get(0));
        // 使用get 遍历集合
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+",");
        }

        System.out.println("---------------------");
        // 替换指定位置的元素
        String str = list.set(0, "李");// 返回替换指定位置的元素
        System.out.println(str);
        System.out.println(list);

        System.out.println("---------------------");
        // 删除指定位置的元素
        String remove = list.remove(0);// 返回删除指定位置的元素
        System.out.println(remove);
        System.out.println(list);

        System.out.println("---------------------");
        // 第一次出现的索引
        int first = list.indexOf("小张");
        System.out.println("first="+first);
        list.add("小张");
        int last = list.lastIndexOf("小张");
        System.out.println("last="+last);

        System.out.println("---------------------");
        List<String> list2 = list.subList(1, 3);// 范围 [ )
        System.out.println(list);
        System.out.println(list2);
    }
}
// 结果
[li, zhang, wang]
[li, zhao, zhang, wang]
---------------------
[li, 小李, 小张, 小王, zhao, zhang, wang]
---------------------
get:li
li,小李,小张,小王,zhao,zhang,wang,---------------------
li
[李, 小李, 小张, 小王, zhao, zhang, wang]
---------------------
李
[小李, 小张, 小王, zhao, zhang, wang]
---------------------
first=1
last=6
---------------------
[小李, 小张, 小王, zhao, zhang, wang, 小张]
[小张, 小王]

Process finished with exit code 0
(2 ) ListIterator

​ ListIterator 是List集合的特有迭代器,支持迭代过程中添加元素和修改元素

方法声明方法描述
hasPrevious()判断是否存在上一个元素
previous()当前指针先向上移动一个单位,然后再取出当前指针指向的元素
next()先取出当前指针指向的元素,然后指针向下移动一个单位
add(E e)把当前有元素插入到当前指针指向的位置上
set(E e)替换迭代器 最后依次返回的元素
(3)集合遍历
3 ArrayList

​ 再实际的开发中,ArrayList 是使用频率最高的一个集合实现类。

(1)原理

​ ArrayLIst 底层维护了一个Object[]用户存储对象,默认数组的长度 为10。当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来容量的1.5倍。

​ 由于底层使用数组实现,在增加和删除的时候会牵扯到数组增容和拷贝元素,所以效率比较慢。但是数组可以直接按索引查找元素,所以查找时较快。

​ 例如:假设向数组的 0 下标位置添加元素,那么原来的下标位置的元素与需要整体往后移动,并且数组可能还要增容。一旦增容,就需要将老数组的内容拷贝到新数组中。所以数组的增删的效率时很低的。

public class Demo01 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        System.out.println(arrayList.size());
        System.out.println(arrayList.isEmpty());
        System.out.println("--------------");
        // 添加
        arrayList.add("乔丹");
        arrayList.add("勒布朗");
        arrayList.add("罗斯");
        System.out.println(arrayList.isEmpty());
        // 获取
        System.out.println("--------------");
        System.out.println(arrayList.get(1));
        System.out.println("--------------");
        //  遍历
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.println(arrayList.get(i));
        }
        System.out.println("--------------");
        // 删除
        Object remove = arrayList.remove(0);
        System.out.println(remove);
        System.out.println(arrayList.size());
        System.out.println("--------------");
        // 清空所有
        arrayList.clear();
        System.out.println(arrayList.isEmpty());
        System.out.println(arrayList.size());
    }
}
// 结果
0
true
--------------
false
--------------
勒布朗
--------------
乔丹
勒布朗
罗斯
--------------
乔丹
2
--------------
true
0

Process finished with exit code 0
(2) 例子
import java.util.ArrayList;

public class Demo03 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("li");
        arrayList.add("zhang");
        arrayList.add("zhang");
        System.out.println(arrayList);
        
        // 取出arraylist集合中重复的元素
        ArrayList<String> arrayList1 = new ArrayList();
        for (int i = 0; i < arrayList.size(); i++) {
            String string = arrayList.get(i);
            if (!(arrayList1.contains(string))) { //包含
                arrayList1.add(string);
            }
        }
        System.out.println(arrayList1);
    }
}
// 结果
[li, zhang, zhang]
[li, zhang]

Process finished with exit code 0
(3) 指定类型
  • <> 泛型----> 广泛(规范) 类型

  • 写法–入门

import java.util.ArrayList;
// 泛型
public class Demo02 {
    public static void main(String[] args) {
        // 写法1 --指定为Person的对象
        ArrayList<Person> arrayList = new ArrayList<>();
        // 写法2
        ArrayList<Person> arrayList1 = new ArrayList<Person>();
        
        arrayList.add(new Person("li",22));
        arrayList.add(new Person("zhang",21));
        //arrayList.add("st"); String类型与指定类型不符
    }
}

注意:

​ 泛型中(<>),其中指定了类型,便不能添加其他类型的数据了。

4 LinkedList
(1)原理

​ 由于 LinkedList 在底层使用链表实现存储结构,需要让上一个元素记住下一个元素,所以每个元素中保存的有下一个元素的位置。虽然 LinkedList 中也有下标,但是查找的时候需要从头往下找,显然时没有数组查找块。但是,链表在插入新元素的时候,值需要让前一个元素记住新元素,让心愿记住下一个元素就可以了,所以插入很快。

(2) 特有方法
方法方法描述
addFirst(E e)增加到集合的最前面
addLast(E e)增加到集合的最后面
getFirst()获得集合的第一个元素
getLast()获得集合的最后一个元素
removeFirst()如果集合中没有原申诉,获取或者删除元素时抛出 NoSuchElementException
removeLast()如果集合中没有原申诉,获取或者删除元素时抛出 NoSuchElementException
descendingllterator()返回逆序的迭代器对象
5 Vector

​ Vector 时一个线程安全的集合。它和 ArrayList 特性相似,较少很用。

(1) 特有的方法
方法声明方法作用
void addElement(E obj)在集合末尾添加元素
E elementAt(int index)返回指定角标的元素
Enumeration element()返回集合 中的所有元素,封装到Enumeration 对象中
(2)Enumeration 接口
方法声明方法作用
boolean hashMoreElements()测试此枚举是否包含更多的元素
E nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素

四 泛型

1 问题引入

​ 在 JDK1.5之前,对象保存到集合中就会失去自身类型信息,从集合中取出元素时需要进行类型的强制转换,这样不可避免就会引发产需的一些安全性问题。

(1)例子

​ 当集合中存储不同的对象类型时,那么在运行时可能对导致强制类型异常(迭代)

(2)原因分析

​ 在集合中可以存储热议对象,对象保存到集合中都将被认为是 Object 类型。如果需要使用对象的特有方法,那么就需要类型转换,但是集合中存入的对象可能是不同的,这将引发类型转换异常。

(3)解决办法

​ 在类型转换的时候,虽然可以通过 if 语句进行类型检查 (instanceof) ,但是效率较低。我们可以通过个容器加限定类型的方式,规定容器只能存储一种类型的对象,就像给容器贴标签说明该容器中只能存储说明杨类型的对象一样。所以在 JDK1.5中出现泛型。

2 泛型应用

(1)泛型语法

​ 以ArrayList< E >为例,其中<>读成 typeof.

  1. ArrayList< E >中的E称为类型参数,整个称为 ArrayList< E > 泛型类型。
  2. ArrayList< String >中的 String 称为实际类型参数,整个ArrayList< String >称为参数化的类型。// 检查作用:是否为String类型
(2)典型应用

​ 在下面例子中使用泛型后,将运行时发生的异常提前至编译时被发现,同时在获取元素的时候无需强制转换,避免了类型转换的异常问题。

A 支持泛型的集合中只能是指定类型元素
B 泛型类型必须是引用类型

​ 泛型类型必须是引用类型,不支持基本类型。如果需要支持基本类型,那么使用基本类型的包装类,如int使用Integer类。

C 集合的方法支持泛型

​ 在集合的实现类中,都对泛型提供了较好的支持,使用泛型后取出元素不与要类型转换。

(3)擦除泛型

​ 泛型通常不会直接用于创建对象,而是使用这个泛型类定义一个具有类型的类。在包含泛型的Java类被编译后,生成的Class文件中使用具体类型替换泛型 (T) 敌营,将会定义一个完整的类,这个过程称之为"擦除"。

3 泛型方法

​ 需求:写一个函数,调用者传递什么类型的变量,该函数就返回什么类型的变量?

(1) 无泛型方案

​ 无法确定具体传递什么类型的数据,那么方法的形参就只能定义为Object类型,返回值也是Object类型的。

public class Demo02 {
    public static void main(String[] args) {
        // 使用方法,因为参数是引用传递,所以基本数据类型自动装箱为Integer(包装类)对象
        Integer number = (Integer) getNumber(1);
        // 在使用number时,自动拆箱为基本数据类型
        System.out.println("number="+number);
    }

    public static Object getNumber(Object object){
        return object;
    }
}
// 结果
number=1

Process finished with exit code 0
(2) 泛型方法
public class Demo01 {
    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();
        System.out.println(demo01.getDate(1));
        System.out.println(demo01.getDate("li"));
        System.out.println(demo01.getDate(new Object()));
    }
    // static不能定义泛型方法
    public<T> T getDate(T t){
        return t;
    }
}
// 结果
1
li
java.lang.Object@1540e19d

Process finished with exit code 0
(3) 泛型方法实例

4 泛型类

​ 如果一个类中多处都要用到同一个泛型,这是可以把泛型定义在类上(即类级别的泛型)

(1) 泛型类
public class Demo02<T> {

    public static void main(String[] args) {
        
    }
    public void pringArray(T[] array){
        System.out.println(Arrays.toString(array));
    }
    
}

5 泛型接口

(1) 泛型接口
public interface MyInterface<T> {
    void print(T t);
}


public class MyInterfaceImpl<T> implements MyInterface<T>{


    @Override
    public void print(T t) {
        
    }
}
(2) 泛型接口实例

6 泛型的限定

<? extends E> 向下限定,E及其子类 <? super E> 向上限定,E及其父类 <?>任意类型
public class Animal {
}
public class Cat extends Animal {
}
public class Dog extends Animal {
}
public class Demo03 {
    public static void main(String[] args) {
        List<Animal> animals = new ArrayList<>();
        List<Cat> cats = new ArrayList<>();
        List<Dog> dogs = new ArrayList<>();
        System.out.println("向下限定");
        print(animals);
        print(cats);
        print(dogs);
        System.out.println("向上限定");
        List<Object> objects = new ArrayList<>();
        print2(animals);
        print2(objects);
        System.out.println("任意类型");
        print3(animals);
        print3(cats);
        System.out.println(objects);

    }
    public static void print(Collection<? extends Animal> collection){
        System.out.println("哈哈");
    }
    public static void print2(Collection<? super Animal> collection){
        System.out.println("哈哈2");
    }
    public static void print3(Collection<?> collection){
        System.out.println("哈哈3");
    }
}
// 
向下限定
哈哈
哈哈
哈哈
向上限定
哈哈2
哈哈2
任意类型
哈哈3
哈哈3
[]

五 Set

1 Set

​ Set 中元素是无序 (无下标) 存放的,即元素存入和取出的不一定相同,并且元素不能重复。其中,HashSet 是一个常用的Set。

​ Set的特点:

  1. Set 中的元素无序,但可以排序。
  2. Set 中的元素无下标。
  3. Set 中的元素不可重复。
(1)存储原理

​ 和 List 不同的是,Set 中的对象不是按元素加入到集合中的先后顺序依次摆放,因此 Set 中是没有索引的,也不能按索引访问集合中的对象。

​ 在 Set 内部的存储结构使用了散列算法 (HASH算法),按对象的散列值决定存放位置,也按照散列检索对象。由于散列值的无序特点,依次 Set 的行为表现为一个无序的集合。

hashCodehashCodehashCodehashCode

​ 例如:有三个创建好的对象要加入到一个Set 中

public class Demo07 {
    public static void main(String[] args) {
        Object object1 = new Object();
        Object object2 = new Object();
        Object object3 = new Object();
    }
}

​ 现在分析 Set 中存放对象的内部行为:

  1. Set set = new HashSet();

    • 最开始是一个空集合

  2. set.add(object1);

    • 现加入object1

    • object1 的hashCode值决定的位置

    • object1
  3. set.add(object2);

    • 现加入object2

    • object2 的hashCode值决定的位置

    • object2object1

核心小结:

  1. HashSet 调用对象的 hashCode() 方法得到对象的 hashCode 值,并依据 hashCode 值决定该对象在 HashSet 中存放的位置。
  2. HashSet 判断两个对象是否相等的依据:两个对象通过 equals() 方法比较后结果为相等,并且两个对象的hashCOde() 方法得到对象的 hashCode值 也相等
  3. 规则:如果两个对象通过 equals() 方法比较后结果为相等,那么两个对象的hashCode() 方法得到对象的hashCode() 值也应该是相等的
  4. 建议:利用 IDE重写 hachCode() 和 equals() 方法
(2)常用方法
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Demo06 {
    public static void main(String[] args) {
        Set<Person> set = new HashSet<>();
        Person p1 = new Person("lifa",20);
        Person p2 = new Person("la",20);
        Person p3 = new Person("lia",20);
        Person p4 = new Person("liaf",20);
        System.out.println("-------------添加");
        set.add(p1);
        set.add(p2);
        System.out.println(set);
        System.out.println("-------------删除");
        set.remove(p2);
        System.out.println(set);
        System.out.println("-------------遍历");
        set.add(p3);
        set.add(p4);
        Iterator<Person> iterator = set.iterator();
        while (iterator.hasNext()) {
            Person next = iterator.next();
            System.out.println(next);
        }
        System.out.println("--------------------");
        System.out.println(set.size());
        System.out.println(set.isEmpty());
        System.out.println("----------------");
        // 重复的前提 泛型中的类 要重写equals 和 hashCode 方法
        Person p5 = new Person("li",20);
        Person p6 = new Person("li",20);
        set.add(p6);
        set.add(p5);
        System.out.println(set);
    }
}
// 结果
-------------添加
[Person{name='la', age=20}, Person{name='lifa', age=20}]
-------------删除
[Person{name='lifa', age=20}]
-------------遍历
Person{name='liaf', age=20}
Person{name='lia', age=20}
Person{name='lifa', age=20}
--------------------
3
false
----------------
[Person{name='liaf', age=20}, Person{name='lia', age=20}, Person{name='li', age=20}, Person{name='lifa', age=20}]

Process finished with exit code 0
(3)特有方法

​ 该集合中没有特有的方法,直接继承 Collection 的方法。

(5)重复元素

​ 再Java中,重复元素使用 Object类中的 hashCode 和 equals 判断。在类中重写了 hashCode 和 equals,当两个的 hashCode 方法返回相同值斌且两个对象的equals 返回true时,两个对象时相同的。

​ 有一些创建的重复元素情形:

  1. 所有 null 常量都是重复元素
  2. 相同对象的不同引用变量是重复元素
  3. String 对象以重写 hashCode 和 euqals() ; 即只要两个String 对象通过 equals() 方法比较后结果为相等,那么这两个String对象的hashCode()等到的哈市Code值也是相等的。简单说:两个String对象的值相同时,就认为这两个String对象是重复的。
import java.util.HashSet;
import java.util.Set;

public class Demo11 {
    public static void main(String[] args) {
        Set set = new HashSet();// 没有指定类型
        set.add(null);
        set.add(null);// null 对象不能重复加入,这是替换--自动封箱
        System.out.println(set.size());

        // 同一个对象的多个引用变量
        Person person = new Person("li",20);
        Person person1 = person;
        set.add(person);
        set.add(person1);
        System.out.println(set);

        // 相同值的String对象
        String string = "hello";
        String string1 = new String("hello");
        System.out.println((string==string1));
        System.out.println(string.equals(string1));
        set.add(string);
        set.add(string1);
        System.out.println(set);
    }
}
// 结果
1
[null, Person{name='li', age=20}]
false
true
[null, Person{name='li', age=20}, hello]

Process finished with exit code 0

2 HashSet

​ 使用 Set 集合都是需要去掉重复元素的。如果在存储的时候逐个 equals() 比较,效率较低,哈希算法提高了去重复的小笼包,降低了使用equals() 方法的次数。当 HashSet 调用 add() 方法存储对象的时候,先调用对象的 hashCode() 方法得到一个哈希值,然后再集合中查找是否由哈希值相同的对象,如果没有韩析置相同的对象就直接村粗集合,如果有哈希值相同的对象,就和哈希值相同的对象诸葛进行equals() 比较,比较结果为false 就存入,true 则不存入。

3 TreeSet

​ TreeSet 是 SortedSet 接口的唯一实现类,TreeSet 可以确保集合元素处于排序状态。TreeSet 支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。

(1)引入

​ 设计以个数据结构用于保存数据,要求不能重复存储元素,并且对数据支持排序。

​ ArrayLIst,LinkedList 不能去除重复数据。

​ HashSet 可以去除重复,但是不支持排序。

​ 这里引入一个新的数据结构,TreeSet。

(2)排序接口

​ 在 Java 中提供了一个对象比较接口 Comparable,它只用一个方法:

方法声明方法描述
int comparableTo(Object o)大于返回正数,等于返回零,小于返回负数

​ 所有加入 TreeSet 的元素必须实现 Comparable 接口,否则会抛出异常,在TreeSet 内部使用 Comparable 接口对元素排序。和 HashSet 相比,它的效率较低。

(3) 实例

​ String本身实现了 Comparable 接口。

​ 实现对Person排序,如果年龄相同就再比较姓名。

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

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

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

    @Override
    public int compareTo(Person o) {
        if (this.getAge() > o.getAge()) {
            System.out.println(1);
            return 1;
        } else if (this.getAge() < o.getAge()) {
            System.out.println(2);
            return -1;
        }else {
            System.out.println(3);
            return this.getName().compareTo(o.getName());
        }
    }
}
public class Demo08 {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("bac");
        set.add("csc");
        set.add("fcd");
        set.add("ays");
        set.add("bac");
        System.out.println("size="+set.size());
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("---------------");
        Set<Person> set1 = new TreeSet<>();
        Person p1 = new Person("lifa",20);
        Person p2 = new Person("la",21);
        Person p3 = new Person("lia",20);
        Person p4 = new Person("lia",20);
        set1.add(p1);
        set1.add(p2);
        set1.add(p3);
        set1.add(p4);
//        System.out.println(set1);
        Iterator<Person> iterator1 = set1.iterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }

    }
}
// 结果
size=4
ays
bac
csc
fcd
---------------
3
1
3
3
3
Person{name='lia', age=20}
Person{name='lifa', age=20}
Person{name='la', age=21}

Process finished with exit code 0

4 LinkedHashSet

​ LinkedHashSet 是具有可与之迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护这一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。

六 Map

​ 在集合框架中,Map 与 Collection 是并列存在的,互相没有继承关系。

​ Map 的特点:

  1. 采用key/values 的形式存储数据
  2. Map 中key不可重复
  3. 各个键值对无序
  4. 各个键值对无下标

1 Map

(1)数据结构

​ 映射可以看到一张多行两列的表格,每一列是"key",第二列是"values"。例如,使用Map集合管理人员信息:

(2)基本操作

​ Map 是一种按照键(key)存储元素的容器,键(key) 可以是任意类型的对象,所有 value 按 key 存储和获取。

方法声明方法描述
V put(K key,V value)按 key增加value。如果在 Map 中已经有相同的 key,则返回以前的对象,并使用 value 覆盖原来的对象。
putAll(Map<? extend K,? extends V> m)将参数m中的所有key/value对加入到集合中
remove(key)删除key对应的 key/value对
value get(key)按key 获取 value。如果没有匹配的key则返回null
clear()清空集合对象
boolean isEmpty()长度为0返回true,否则返回false
int size()返回 Map 中key/value 对的个数
boolean containsKey(Object key)判断集合中是否包含指定的key
boolean containsValue(Object value)判断集合中是否包含指定的value
mport java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Demo09 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("中国","北京");
        map.put("俄罗斯","莫斯科");
        map.put("美国","纽约");
        // 不允许键重复
        map.put("美国","白宫");
        System.out.println(map);

        System.out.println("------------");
        // 依据键获取值
        String string = map.get("中国");
        System.out.println(string);
        System.out.println("------------");
        // 包含 contians
        System.out.println(map.containsKey("中国"));
        System.out.println(map.containsValue("北京"));
        System.out.println("------------");
        // keySet() 获取键的Set
        Set<String> strings = map.keySet();
        System.out.println(strings);
        System.out.println("------------");
        // 迭代
        Iterator<String> iterator = strings.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            String string1 = map.get(next);
            System.out.println(next+" "+string1);
        }
        System.out.println("------------");
        // 删除 依据键删除
        String re = map.remove("美国");
        System.out.println(re);
        System.out.println(map);
    }
}
// 结果
{美国=白宫, 俄罗斯=莫斯科, 中国=北京}
------------
北京
------------
true
true
------------
[美国, 俄罗斯, 中国]
------------
美国 白宫
俄罗斯 莫斯科
中国 北京
------------
白宫
{俄罗斯=莫斯科, 中国=北京}

Process finished with exit code 0
(3) 遍历操作
方法声明方法描述
Set< K > keySet()获得所有 key 组成Set集合
Collection< V > values()获取所有 value 组成的集合
Set< Map.Entry< K,V > > entrySet()获取所有 Map.Entry 组成的集合。Map.Entry 由 key,value 组成,通过 getKey,getValue获取其键和值
使用 keySet()

​ 这是最常用的Map迭代实现方式。

​ 将key转成Set集合,通过迭代器取出Set集合中的每一个key,再通过key获取对应的value。

public class Demo09 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("中国","北京");
        map.put("俄罗斯","莫斯科");
        map.put("美国","纽约");
        // 不允许键重复
        map.put("美国","白宫");
        System.out.println(map);
        
         // keySet() 获取键的Set
        Set<String> strings = map.keySet();
        System.out.println(strings);
        Iterator<String> iterator3 = strings.iterator();
        while (iterator3.hasNext()) {
            String key = iterator3.next();
            System.out.println(map.get(key));
        }
        
    }
}
使用 values()

​ 将 value 转成Set集合,通过迭代器取出Set集合中的每一个value

public class Demo09 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("中国","北京");
        map.put("俄罗斯","莫斯科");
        map.put("美国","纽约");
        // 不允许键重复
        map.put("美国","白宫");
        System.out.println(map);
        
            // values() 迭代器遍历
        Collection<String> values = map.values();
        System.out.println(values );
        Iterator<String> iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            String next = iterator1.next();
            System.out.println(next);
        }
        
    }
}
使用 Map.Entry 对象

​ 这种方式一般不使用

​ 将 key,value 转成Set集合(entrySet()),通过迭代器取出Set集合中的每一个 entrySet

 public class Demo09 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("中国","北京");
        map.put("俄罗斯","莫斯科");
        map.put("美国","纽约");
        // 不允许键重复
        map.put("美国","白宫");
        System.out.println(map);
        // entrySet() 获得所有记录,条目
        // 方式1
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> iterator2 = entrySet.iterator();
        while (iterator2.hasNext()) {
            Map.Entry<String, String> next = iterator2.next();
            System.out.println(next);
        }
        // 方式2
        while (iterator2.hasNext()) {
            Map.Entry<String, String> entry1 = iterator2.next();
            String key = entry1.getKey();
            String value = entry1.getValue();
            System.out.println(key+","+value);
        }
        
    
}
}

2 HashMap

​ HashMap 是是哟个最广泛的 Map 实现,它的底层是哈希表数据结果实现存储,不支持多线程同步,可以存入null键,null值。

import java.util.HashMap;

public class Demo10 {
    public static void main(String[] args) {
        HashMap<Person,String> hashMap = new HashMap<>();
        hashMap.put(new Person("li",20),null);
        hashMap.put(new Person("zhang",21),"北京");
        System.out.println(hashMap);
    }
}
// 结果
{Person{name='li', age=20}=null, Person{name='zhang', age=21}=北京}

Process finished with exit code 0

3 LinkedHashMap

​ LinkedHashMap 底层使用链表实现。

4 TreeMap

​ HashMap 中的所有数据都是无序存放的,在TreeMap中按照key 进行排序。要求key必须实现Comparable接口,具体要求见TreeMap中的实现要求。

七 工具类

1 Collections

​ Collections 不是 Collection 接口的实现类,它是一个工具类,其中所有方法都是静态的。

注意: Collections he Collection 的区别

(1)List 集合操作

​ 只适用于 List 集合的工具方法。

功能方法方法描述
二分查找int binarySearch(list,key)集合中元素都是 Comparable 的实现类,且元素要按升序排列
排序sort(list)集合中元素都是 Comparable 的实现类
顺序反转reverse(list)集合中元素都是 Comparable 的实现类
强行逆转Comparator reverseOrder(Comparator)按 Comparator 比较方式逆转
位置交换swap(list,x,y)接索引文职交换
元素替换replaceAll(list,old,new)如果替换的元素不存在,那么原集合不变
(2)其他工具方法
功能方法方法描述
最大值max(Collection) 或者 max(Collection,comparator)
最小值min(Collection) 或者 min(Collection,comparator)
同步集合Set synchronizedSet(Set< T > s) ,Map synchronizedMap(Map< K,V > m),List synchronizedList(List< T > list)将不同步的集合变成同步的集合
(3)实例
import java.util.*;

public class Demo01 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(0);
        list.add(8);
        list.add(1);
        list.add(2);
        System.out.println("-----------------------排序");
        Collections.sort(list);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer next = iterator.next();
            System.out.println(next);
        }

        System.out.println("-----------------------集合转数组");
        Object[] objects = list.toArray();
        System.out.println(Arrays.toString(objects));

        System.out.println("-----------------------数组转集合");
        Integer[] arr = new Integer[]{2,5,8};
        List<Integer> list3 = Arrays.asList(arr);
        System.out.println(list3);
        list.addAll(list3);
        System.out.println(list);

        System.out.println("-----------------------二分查找");
        int index = Collections.binarySearch(list, 8);
        System.out.println("index="+index);
        int index2 = Collections.binarySearch(list, 6);
        System.out.println("index2="+index2);
        System.out.println("-----------------------顺序反转1");
        Collections.reverse(list);
        for (Integer i:list
             ) {
            System.out.println(i);
        }
        System.out.println("-----------------------顺序反转2");

        System.out.println("-----------------------位置交换");
        Collections.swap(list,0,1);
        System.out.println(list);
        System.out.println("-----------------------元素替换");
        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(3);
        list2.add(4);
        list2.add(7);
        Collections.sort(list2);
        Collections.replaceAll(list,0,9);// 只能替换引用类型
        System.out.println(list);
        System.out.println("-----------------------最大值");
        Integer max = Collections.max(list);// 装箱
        System.out.println("max="+max);// 拆箱
        System.out.println("-----------------------最小值");
        Integer min = Collections.min(list);
        System.out.println("min="+min);
        System.out.println("-----------------------使用指定元素替换集合中的所有元素");
        Collections.fill(list,3);
        System.out.println(list);

    }
}
// 结果
-----------------------排序
0
1
2
5
8
-----------------------集合转数组
[0, 1, 2, 5, 8]
-----------------------数组转集合
[2, 5, 8]
[0, 1, 2, 5, 8, 2, 5, 8]
-----------------------二分查找
index=7
index2=-8
-----------------------顺序反转1
8
5
2
8
5
2
1
0
-----------------------顺序反转2
-----------------------位置交换
[5, 8, 2, 8, 5, 2, 1, 0]
-----------------------元素替换
[5, 8, 2, 8, 5, 2, 1, 9]
-----------------------最大值
max=9
-----------------------最小值
min=1
-----------------------使用指定元素替换集合中的所有元素
[3, 3, 3, 3, 3, 3, 3, 3]

Process finished with exit code 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

咸鱼不咸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值