JavaSE-集合

目录

[TOC]

第一章:数据结构基础

1.1-什么是数据结构

​ 数据结构就是计算机存储、组织数据的方式

​ 指的是相互之间存在着特定关系的一种或多种的数据元素集合。

1.2-为什么要学习数据结构

通常情况下,精心选择合适的数据结构可以带来更高的运行或存储的效率

数据结构往往同高效的检索算法索引技术有关。

1.3-数据结构-栈

栈结构介绍

  • :栈(stack)又名堆栈,是一种运算受限的线性表
  • 受限:限定仅在表尾进行插入和删除操作的线性表(这一端被称为栈顶,另一端称为栈底

特性

特性:先进后出

1.4-数据结构-队列

队列介绍

  • 队列:是一种受限的特殊线性表。
  • 受限:只允许在表的前端(队头)进行删除操作,后端(队尾)进行插入操作。

队列特性

特性:先进先出

1.5-数据结构-数组

数组介绍

数组:一组有序的(索引有序并且从0开始)类型相同的长度固定的元素集合。

特性

  • 元素有序
  • 元素同类型
  • 长度固定

应用效果

  • 查询快
    • 从数组索引0开始查找,根据指定位置的偏移量可快速获取数据。
  • 增删慢
    • 数组的长度是固定的,若删除或增加一格元素,则会先创建一个新的数组,再把原数组的数据根据操作复制到新数组中。

1.6-数据结构-链表

链表介绍

  • 链表:链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的

  • 单向链表:单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

  • 双向链表:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表

链表特性

  • 由一系列结点(链表中的每一个元素称为结点)组成。
  • 结点可以在运行时动态生成。
  • 每个结点包括两个部分(单链表)
    • 一个是存储数据元素的数据域
    • 另一个是存储下一个结点地址的指针域。

应用效果

  • 查询慢:链表的地址不是连续的,每次查找都得从头开始查找。
  • 增删快:增删操作不会影响链表的整体结构。

1.7-数据结构-红黑树

介绍

红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组

特性

  • 查询速度快
    • 在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。

第二章:泛型介绍

2.1-什么是泛型

泛型,未知的类型,可以在类或方法中预支地使用未知的类型。

2.2-泛型的好处

可以避免类型转换的麻烦。

2.3-定义和使用含有泛型的类

定义

修饰符 class 类名<代表泛型的变量> { }

使用

在创建对象的使用。也就是在创建对象时确定泛型的类型。

public class ArrayList<E> {
    public boolean add(E e){ }
    ...
}

public class MainDemo{
  public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("张三");
  }
}

2.4-定义和使用含有泛型的方法

定义

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }

使用

public static void main(String[] args) {
   methon1("张三");
}
public static  <M> void methon1(M m){
   System.out.println(m);
}

2.5-定义和使用含有泛型的接口

定义

修饰符 interface接口名<代表泛型的变量>

使用

public interface TestInterface<T> {
  public abstract void show(T t);
}

public class<T> TestImpl implements TestInterface<T> {
  @Override
  public T void show(T o) {
    System.out.println(o);
  }
}

public class Main01 {
  public static void main(String[] args) {
    TestImpl<String> t = new TestImpl<>();
    t.show("我的信息");

  }
  
}

第三章:单列集合-Collection系列

3.1-集合介绍

  • 定义:在Java中,集合是一种可以存储多个数据的容器。

  • 代码:

        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("王五");
        list.add("赵六");
        System.out.println(list); // [张三, 王五, 赵六]
    

3.2-集合和数组的区别

  • 区别:数组的长度是固定的,集合的长度是可变的

  • 代码:

        String[]strs = new String[2];
        strs[0] = "张三";
        strs[1] = "李四";
        // strs[2] = "赵六"; // 运行时报错java.lang.ArrayIndexOutOfBoundsException
    
        ArrayList<String> list = new ArrayList<>();
        System.out.println(list.size()); // 0
        list.add("张三");
        list.add("王五");
        list.add("赵六");
        System.out.println(list.size());  // 3
    

3.3-集合框架Collection

概述

10.jpg
  • 单列集合类的根接口
  • 有两个子接口
    • java.util.List;特点是:内容可重复,有序
    • java.util.Set; 特点是:内容不可重复,无序
  • 定义了单例集合最共性的方法。

通用方法

方法:

  • public boolean add(E e): 把给定的对象添加到当前集合中 。
  • public void clear() :清空集合中所有的元素。
  • public boolean remove(E e): 把给定的对象在当前集合中删除。
  • public boolean contains(E e): 判断当前集合中是否包含给定的对象。
  • public boolean isEmpty(): 判断当前集合是否为空。
  • public int size(): 返回集合中元素的个数。
  • public Object[] toArray(): 把集合中的元素,存储到数组中。

代码:

  List<String> list = new ArrayList<>();
    // 添加元素
    list.add("张三");
    list.add("李四");
    System.out.println(list);
    // 移除元素
    list.remove("张三");
    System.out.println(list);
    // 判断集合中是否包含某个元素
    boolean isHas = list.contains("张三");
    System.out.println(isHas); // false
    // 判断当前集合是否为空
    boolean isEmpty = list.isEmpty();
    System.out.println(isEmpty);
    // 清空元素
    list.clear();
    System.out.println(list);
    // 集合的长度
    System.out.println(list.size());
    // 集合中的元素存储到一个数组中
    Object[]s =  list.toArray();

遍历集合

  • Iterator方式

    • 介绍:Iterator,是一个迭代器接口。Collection中的成员方法iterator()被调用后,会返回一个Iterator对象。利用这个对象可以实现遍历集合。如何遍历呢?在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

    • Iterator对象的成员方法:

      • hasNext(); 检测集合中是否存在下一个元素
      • next(); 找到并获取下一个元素
    • 迭代器的原理:在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。

    • 代码:

          List<String> list = new ArrayList<>();
          list.add("张三");
          list.add("李四");
          list.add("王五");
          // 得到一个迭代器对象
          Iterator<String> it = list.iterator();
          while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
          }
      
  • 增强for方式

    • 介绍:增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

    • 使用格式

      for(元素的数据类型  变量 : Collection集合or数组){ 
          //写操作代码
      }
      
    • 代码:

      for (String name:list) {
        System.out.println(name);
      }
      

3.4-List集合

介绍

​ List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操 作集合的特有方法,如下:

方法

  1. public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
  2. public E get(int index) :返回集合中指定位置的元素。
  3. public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
  4. public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素

特点

  • 存取顺序一致,并且有索引。
  • 元素内容可重复。

代码

    List list = new ArrayList();
    list.add("a");
    list.add("b");
    list.add("c");
    // public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
    list.add(1,"d");
    System.out.println(list); // [a, d, b, c]
    // public E get(int index) :返回集合中指定位置的元素。
    System.out.println(list.get(2)); // b
    // public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
    list.remove(1);
    System.out.println(list); // [a, b, c]
    // public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素
    list.set(1,"B");
    System.out.println(list); // [a, B, c]

List集合实现类-ArrayList

java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,用于日常开发中使用最多的功能为 查询数据、遍历数据,所以 ArrayList 是最常用的集合。

许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

List集合实现类-LinkedList

java.util.LinkedList 集合数据存储的结构是链表结构。方便元素添加、删除的集合。

常用方法:

  1. public void addFirst(E e) :将指定元素插入此列表的开头。
  2. public void addLast(E e) :将指定元素添加到此列表的结尾。
  3. public E getFirst() :返回此列表的第一个元素。
  4. public E getLast():返回此列表的最后一个元素。
  5. public E removeFirst() :移除并返回此列表的第一个元素。
  6. public E removeLast() :移除并返回此列表的最后一个元素。
  7. public E pop() :从此列表所表示的堆栈处弹出一个元素。
  8. public void push(E e):将元素推入此列表所表示的堆栈。
  9. public boolean isEmpty():如果列表不包含元素,则返回true。

代码:

 LinkedList list = new LinkedList();
    list.add("a");
    list.add("b");
    // public void addFirst(E e) :将指定元素插入此列表的开头。
    list.addFirst("A");
    // public void addLast(E e) :将指定元素添加到此列表的结尾。
    list.addLast("B");
    System.out.println(list); // [A, a, b, B]
    // public E getFirst() :返回此列表的第一个元素。
    System.out.println(list.getFirst()); // A
    // public E getLast() :返回此列表的最后一个元素。
    System.out.println(list.getLast()); // B
    // public E removeFirst() :移除并返回此列表的第一个元素。
    list.removeFirst();
    // public E removeLast() :移除并返回此列表的最后一个元素。
    list.removeLast();
    System.out.println(list); //[a, b]
    // public E pop() :从此列表所表示的堆栈处弹出一个元素。
    list.pop();
    System.out.println(list); // [b]
    // public void push(E e) :将元素推入此列表所表示的堆栈。
    list.push("a");
    System.out.println(list); // [a, b]
    // public boolean isEmpty() :如果列表不包含元素,则返回true。
    System.out.println(list.isEmpty()); // false

3.5-Set集合

概述

  • 继承了Collectin集合
  • 没有扩充方法
  • 与List集合不同的是会保证集合中元素的唯一性

HashSet

介绍:

  • 集合中的元素存取是无序的

  • 集合中的元素是不重复的

  • HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCodeequals 方法。

  • 代码:

        Set set = new HashSet();
        set.add("张三");
        set.add("李四");
        set.add("李四");
        set.add("王五");
        set.add("赵六");
        System.out.println(set);  // [李四, 张三, 王五, 赵六]
    

哈希值:

  • 一个十进制的逻辑地址。

  • 所有的对象都继承里Object中的HashCode方法

  • 代码:

        System.out.println("a".hashCode());  // 97
        System.out.println("b".hashCode()); // 98
        System.out.println("张三".hashCode()); // 774889
        System.out.println("李四".hashCode()); // 842061
        int[]arr1={1,2,3};
        System.out.println(arr1.hashCode()); //1355531311
    

存储结构:数组+链表/红黑树,在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。 但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找 时间。

总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一, 其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一, 就必须复写hashCode和equals方法建立属于当前对象的比较方式。

代码:要求自定义人物类型(含有姓名、年龄),用HashSet集合存储,若对象的姓名和年龄一致则在集合中不允许重复

/*人物类*/
public class People {
  private String name;
  private int age;
  public People(String name, int age) {
    this.name = name;
    this.age = age;
  }
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    People people = (People) o;
    return age == people.age &&
            name.equals(people.name);
  }
  @Override
  public int hashCode() {
    return Objects.hash(name, 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;
  }
}

// 入口类
public class Main {
  public static void main(String[] args) {
    People p1 = new People("张三",10);
    People p2 = new People("李四",12);
    People p4 = new People("李四",12);
    People p3 = new People("王五",10);

    HashSet set = new HashSet();
    set.add(p1);
    set.add(p2);
    set.add(p3);
    set.add(p4);
    System.out.println(set.size()); // 3
  }
}

LinkedHashSet

  • 组织结构:哈希表(数组+链表/红黑树) + 链表(记录存取顺序)

  • 特点:

    • 元素唯一性
    • 元素存取有序。
  • 代码:

        LinkedHashSet set = new LinkedHashSet();
        set.add("张三");
        set.add("李四");
        set.add("李四");
        set.add("王五");
        set.add("赵六");
        System.out.println(set);  // [张三, 李四, 王五, 赵六]
    

其他扩展-可变参数

  • 格式:

    修饰符 返回值类型 方法名(参数类型... 形参名){ }
    等价于
    修饰符 返回值类型 方法名(参数类型[]形参名){ }
    
  • 代码:

      public static void main(String[] args) {
        System.out.println(add(1,2,3));  // 6
        System.out.println(add(1,200,300,400));  // 901
      }
    
      public  static int add(int...num) {
        int sum = 0;
        for (int i = 0; i < num.length; i++) {
          sum += num[i];
        }
        return sum;
      }
    
  • 原理:在编译成class文件时,源代码中的可变参数会自动变成数组。

  • 注意事项:

    • 可变参数类型要一致
    • 可变参数要放在参数列表最后

3.6 Collections

介绍

java.utils.Collections 是集合工具类,用来对集合进行操作 。常用的方法如下:

  1. public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
  2. public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。
  3. public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
  4. public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

代码

   ArrayList<Integer> list = new ArrayList<>();
    //public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
    Collections.addAll(list, 1, 3, 4, 5, 6, 8, 7);
    System.out.println(list); // [1, 3, 4, 5, 6, 8, 7]
    //public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。
    Collections.shuffle(list);
    System.out.println(list); // 随机顺序[5, 4, 6, 3, 7, 1, 8]
    //public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
    Collections.sort(list);
    System.out.println(list); // 默认升序[1, 3, 4, 5, 6, 7, 8]
    //public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照指定规则排序。
    Collections.sort(list, new Comparator<Integer>() {
      @Override
      public int compare(Integer o1, Integer o2) {
        return o2-o1;
      }
    });
    System.out.println(list);

注意事项

若要对自定义对象进行排序时,使用 sort(List<T> list)时,自定义类型需要实现Comparable接口,并且要重新CompareTo方法

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

  public People(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 int compareTo(People o) {
    return this.age - o.age;  // 升序
   // return o.age-this.age; 降序

  }
}

第四章:双列集合-Map系列

4.1-Map集合介绍

  • Map集合是一个存储数据的容器。
  • Map集合存储数据的方式是键值对(key/value)
    • key键不可以重复(若重复设置,则会覆盖原有key对应的值)。
    • value值可以重复。
  • Map集合的底层数据结构是哈希表(数组+链表/红黑树)。

4.2-Map集合常用子类

HashMap

  • Map集合的一个子类
  • 底层数据结构是哈希表
  • 键不可以重复(键对应的类型需要重写了hashCode()和equals方法()),值可以重复。
  • 存取是无序的(存取顺序可能不一致)

LinkedHashMap

  • HashMap集合的一个子类
  • 底层数据结构是哈希表+链表(记录存取顺序)
  • 键不可以重复(键对应的类型需要重写了hashCode()和equals方法()),值可以重复。
  • 存取是有序的(存取的顺序一定是一致的)

注意事项

Map接口中的集合都有两个泛型变量,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量的数 据类型可以相同,也可以不同

public interface Map<K, V>{}

4.3-Map集合常用方法

方法

  1. public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
  2. public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
  3. public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
  4. public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
  5. public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。

代码

   // 创建HashMap对象
    HashMap<String,String> hash = new HashMap<>();
    // 【添加数据-public V put(K key, V value)】
    hash.put("郭靖","华筝");
    hash.put("郭靖","黄蓉"); // 会覆盖之前重复的键值对
    hash.put("杨过","小龙女");
    hash.put("张无忌","赵敏");
    hash.put("宋青书","周芷若");
    System.out.println(hash); // {杨过=小龙女, 宋青书=周芷若, 郭靖=黄蓉, 张无忌=赵敏}
    // 【移除数据-public V remove(Object key)】
    hash.remove("宋青书");
    System.out.println(hash); // {杨过=小龙女, 郭靖=黄蓉, 张无忌=赵敏}
    // 【根据指定的键获取对应的值-public V get(Object key)】
    String value = hash.get("郭靖");
    System.out.println(value); // 黄蓉
    // 【获取Map集合中所有的键存储到Set集合中-public Set<K> keySet()】
    Set<String> set = hash.keySet();
    Iterator<String> iterator = set.iterator();
    while ((iterator.hasNext())){
      String key = iterator.next();
      System.out.println(key + "-" + hash.get(key));
    }
    // 【获取Map集合中的Entry对象-public Set<Map.Entry<K,V>> entrySet()】
    // Map集合中存储了一组Entry对象,entry对象包装了每一对key/value
    // Entry对象可以通过getKey()方法获取键,通过getValue方法获取对应的值
    Set<Map.Entry<String,String>> set2 = hash.entrySet();
    for (Map.Entry<String,String> entry:set2) {
      System.out.println(entry.getKey() + '|'+entry.getValue());
    }

4.4-Map集合中定义自定义类型的键值对

对应自定义类型的键的类型中需要重新hashCode和equals方法

//【执行类】
public class Main {
  public static void main(String[] args) {
    // 存储一组学生信息,要求集合中不允许出现同名同年龄的键
    HashMap<Student,Integer> hash = new HashMap<>();
    hash.put(new Student("张三",18),10010);
    hash.put(new Student("张三",18),10086);
    hash.put(new Student("李四",17),10010);
    hash.put(new Student("王五",17),10010);
    System.out.println(hash.size()); // 3个
  }
}
//【学生类】
public class Student {
  private String name;
  private int age;
  public Student() {}
  public Student(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;
    Student student = (Student) o;
    return age == student.age &&
            name.equals(student.name);
  }
  @Override
  public int hashCode() {
    return Objects.hash(name, age);
  }
}

4.5-LinkedHashMap

  • 介绍:是HashMap的子类,存取数据是有序的。

  • 代码:

        LinkedHashMap<String,String> hash = new LinkedHashMap<>();
        hash.put("郭靖","黄蓉");
        hash.put("杨过","小龙女");
        hash.put("张无忌","赵敏");
        hash.put("宋青书","周芷若");
        System.out.println(hash);//{郭靖=黄蓉, 杨过=小龙女, 张无忌=赵敏, 宋青书=周芷若}
    

4.6-HashTable

  • HashTable集合

    • 底层是线程是安全的,单线程,执行速度慢
    • 底层数据结构是哈希表
    • 之前学习的集合可以存储null键和null值,但是HashTable不可以
  • 代码:

        HashMap<String,Integer>  map = new HashMap<>();
        map.put(null,1);
        map.put("a",null);
        System.out.println(map);
        Hashtable<String,Integer> table = new Hashtable<>();
        table.put(null,1); // 报错NullPointerException
        table.put("a",null);
        System.out.println(table);
    

4.7-JDK9对集合添加的优化

  • 在创建少量元素的集合时,使用JDK9中提供的静态方法of添加更加合适

  • 注意事项:

    • of方法只能被接口List、Set、Map接口调用,不能用子类或实现类调用。
    • of方法初始化后的集合不能更改。
  • 代码:

        List<String> list = List.of("张三","李四","王五","赵六");
        Set<String> set = Set.of("张三","李四","王五","赵六");
        Map<String,Integer> map = Map.of("张三",10,"李四",12);
        System.out.println(list); // [张三, 李四, 王五, 赵六]
        System.out.println(set);  // [李四, 赵六, 张三, 王五]
        System.out.println(map);  // {张三=10, 李四=12}
    

推荐阅读更多精彩内容