Java之集合


集合类体系分为List、Set和Map三个接口,其中List和Set归类为Collection。

  • List 有序、可重复的集合,实现类为ArrayList、LinkedList和Vector等;
  • Set 无序、不可重复的集合,实现类为HashSet、TreeSet和LinkedHash等;
  • Map 键值对存储的集合,实现类为HashMap和TreeMap;

List

List接口是一个有序的Collection,线性列表接口,能够精确的控制每个元素插入的位置,能够通过索引来访问LIst中的元素,第一个元素的索引为0,而且允许有相同的元素,接口存储一组不唯一,有序的对象。

常见的实现类:

  • ArrayList:

    • 基于数组实现,是一个动态的数组队列,但和java中的数组又不一样,它的容量可以自动增长;
    • 可以存储任意多的对象,但是只能存储对象,不能存储原生数据类型例如int;
  • LinkedList:

    • 基于链表数据结构,一个双向链表,链表数据结构的特点是每个元素分配的空间不必连续;
    • 插入和删除元素时速度非常快,但访问元素的速度较慢;
  • 常见List API语法

    import java.util.ArrayList;
    import java.util.LinkedList;
    
    public class ListTest {
    
        public static void main(String[] args) {
    
            ArrayList<String> arrayList = new ArrayList<>();
            LinkedList<String> linkedList = new LinkedList<>();
    
            // 添加元素
            arrayList.add("Laobai");
            arrayList.add("AILynn");
            arrayList.add("PLScript");
            System.out.println(arrayList);
            linkedList.add("Laobai");
            linkedList.add("AILynn");
            linkedList.add("PLScript");
            System.out.println(linkedList);
    
            // LinkedList特有API 获取第一个元素和获取最后一个元素,其他与ArrayList一致
            System.out.println(linkedList.getFirst());
            System.out.println(linkedList.getLast());
    
            // 更新一个元素
            arrayList.set(1,"Lynn");
            System.out.println(arrayList);
            // 返回大小
            System.out.println(arrayList.size());
            // 根据索引获取元素
            System.out.println(arrayList.get(1));
            // 根据索引删除一个元素
            System.out.println(arrayList.remove(1));
            // 根据对象删除元素
            arrayList.remove("Laobai");
            System.out.println(arrayList);
            // 清空元素
            arrayList.clear();
            System.out.println(arrayList);
            // 判断是否为空
            System.out.println(arrayList.isEmpty());
    
        }
    }

    执行结果:

    [Laobai, AILynn, PLScript]
    [Laobai, AILynn, PLScript]
    Laobai
    PLScript
    [Laobai, Lynn, PLScript]
    3
    Lynn
    Lynn
    [PLScript]
    []
    true

ArrayListLinkedList两者的异同:

  • 两个都是List的接口,两个都是非线程安全的;
  • ArrarList是基于动态数组的数据结构,而LinkedList是基于链表的数据结构;
  • 对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;
  • 对于增删操作,LinkedList优于ArrayList

Map

什么是Map数据结构:底层就是一个数组结构,数组的每一项又是一个链表,即数组和链表的结合体;Table是数组,数组的元素是Entry。Entry元素是一个Key-value键值对,它持有一个指向下一个Entry元素的引用,table数组的每个Entry元素同时也作为当前Entry链表的首节点,也指向了该链表的下一个Entry元素。

常见实现类:

  • HashMap
    • 一个散列表(数组和链表),它存储的内容是键值对(key-value)映射
    • 是基于hashing的原理,使用put(key,value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当put()方法传递键和值时,会先对键调用hashCode方法,计算并返回hashCode是用于找到Map数组的bucket位置来存储Entry对象的,是非线程安全的,所以HashMap操作速度很快。
  • TreeMap
    • 在数据的存储过程中,能够自动对数据进行排序,实现了SotredMap接口,它是有序的集合;
    • TreeMap使用的存储结构是平衡二叉树;
    • 默认排序规则:按照key】的字典顺序来排序(升序),也可以自定义排序,要实现Comparator接口。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class MapTest {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
//        TreeMap<String,String> map1 = new TreeMap<>();

        // 往map中存放key-value
        map.put("Laobai","北京");
        map.put("AILynn","天津");
        // 根据key获取value
        String getValue = map.get("Laobai");
        System.out.println("getValue = " + getValue);
        // 判断是否包含某个key
        System.out.println(map.containsKey("AILynn"));
        // 返回map的元素数量
        System.out.println(map.size());
        // 获取所有value集合
        System.out.println(map.values());
        // 返回所有key的集合
        System.out.println(map.keySet());
        // 返回一个Set集合,集合的类型为Map.Entry,是Map声明的一个内部接口
        // 接口为泛型,定义为Entry<K,V>,它表示Map中的一个实体(key-value),主要有getKey和getValue方法
        Set<Map.Entry<String,String>> ertrySet = map.entrySet();
        System.out.println(ertrySet);
        // 清空容器
        map.clear();
        // 判断map是否为空
        System.out.println(map.isEmpty());
    }
}

执行结果:

getValue = 北京
true
2
[天津, 北京]
[AILynn, Laobai]
[AILynn=天津, Laobai=北京]
true

Set

Set相对于List是简单的一种集合,具有和Collection完全一样的接口,只是实现上不同,Set不保存重复的元素,存储一组唯一、无序的对象。Set中的元素是不能重复的,实现细节可以参考Map,因为这些Set的实现都是对应的Map的一种封装。比如HashSet是对HashMap的封装,TreeSet对应TreeMapSet底层是HashMap,由于HashMapPut()方法是一个键值对,当新放入HashMapEntrykey与集合中原有Entrykey相同(hashCode()返回值相等,通过equals比较也返回true),新添加的Entryvalue会将覆盖原来Entryvalue,但key不会有任何改变。Set允许包含值为null的元素,但最多只能有一个null元素。

常用的实现类

  • HashSet
    • HashSet类按照哈希算法来存取集合中的对象,存取速度比较快;
    • 对应的MapHashMap,是基于Hash的快速元素插入,元素无顺序;
  • TreeSet
    • TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序;
import java.util.HashSet;

public class SetTest {
    public static void main(String[] args) {

        HashSet<String> set = new HashSet<>();
        // 添加元素
        set.add("Laobai");
        set.add("AILynn");
        set.add("PLScript");
        System.out.println(set);
        // 返回大小
        System.out.println(set.size());
        // 根据对象删除元素
        System.out.println(set.remove("Laobai"));
        // 清空元素
        set.clear();
        // 判断是否为空
        System.out.println(set.isEmpty());

    }
}

执行结果:

[AILynn, PLScript, Laobai]
3
true
true

HashSet和TreeSet的异同:

  • HashSet不能保证元素的排列顺序,TreeSetSortedSet接口的唯一实现类,可以确保集合元素处于排序状态;
  • HashSet底层用的是哈希表,TreeSet采用的数据结构是二叉树;
  • HashSet中元素可以是null,但只能有一个,TreeSet不允许放入null
  • 一般情况下都使用HashSet,只有在需要排序功能时,才使用TreeSet(性能原因)

迭代器

迭代器:Iterator是java中的一个接口,核心作用就是用来遍历容器的元素,当容器实现了Iterator接口后,可以通过调用Iterator()方法获取一个Iterator对象。

由于容器的实现有多种,不同的容器遍历规则不一样,比如ArrayList/LinkedList/HashSet/TreeSet等,所以设计了Iterator接口,让容器本身实现这个接口,实现里面的方法,即可以不用关心容器的遍历机制,执行使用对应的方法即可。

核心方法:

  • boolean hashNext():用于判断iterator内是否有下个元素,如果有则返回true,没有则返回false;
  • Obejct next():返回Iterator的下一个元素,同时指针也会向后移动1位;
  • void remove():删除指针的上一个元素(容易出问题,删除元素不建议使用容器自己的方法)(需要注意是否有上一个元素,需先next()remove())。
import java.util.*;

public class IteratorTest {
    public static void main(String[] args) {
        testSet();
        testList();
        testMap();
    }

    public static void testSet(){
        Set<String> set = new HashSet<>();
        set.add("Laobai");
        set.add("AILynn");
        set.add("PLScript");
        set.add("NichengWe");

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

    public static void testList(){
        List<String> list = new ArrayList<>();
        list.add("NichengWe");
        list.add("Laobai");
        list.add("AILynn");
        list.add("PLScript");
        list.add("NichengWe");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
            if ("NichengWe".equals(str)){
                iterator.remove();
            }
        }
        System.out.println(list);
    }

    public static void testMap(){
        Map<String,String> map = new HashMap<>();
        map.put("Laobai","老百");
        map.put("AILynn","AI琳");
        map.put("PLScript","漂亮的脚本");

        Set<Map.Entry<String,String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String,String>> iterator = entrySet.iterator();
        while (iterator.hasNext()){
            Map.Entry<String,String> entry = iterator.next();
            String str = entry.getKey() + " = " + entry.getValue();
            System.out.println(str);
        }

    }
}

执行结果:

AILynn
NichengWe
PLScript
Laobai
NichengWe
Laobai
AILynn
PLScript
NichengWe
[Laobai, AILynn, PLScript]
AILynn = AI琳
PLScript = 漂亮的脚本
Laobai = 老百

迭代器与for循环:for循环适合顺序访问,或者通过下标进行访问;迭代器适合链式结构。

Collections工具类

Collection是接口,提供了对集合对象进行基本操作的通用接口方法,List、Set等多种具体的实现类;

Collections是工具类,专门操作Collection接口实现类里面的元素。

常用方法:

  • 排序 sort(List list)

    • 按自然排序的升序排序

      import java.util.*;
      
      public class IteratorTest {
          public static void main(String[] args) {
              testSort();
          }
      
          public static void testSort(){
              List<String> list = new ArrayList<>();
              list.add("LLLL");
              list.add("AAAA");
              list.add("YYYY");
              list.add("NNNN");
              list.add("IIII");
              System.out.println(list);
              Collections.sort(list); // 操作的是容器本身
              System.out.println(list);
          }
      }
    • sort(List list, Comparator c)自定义排序规则,由Comparator控制排序逻辑

      import java.util.*;
      
      public class IteratorTest {
          public static void main(String[] args) {
              testSort();
          }
      
          public static void testSort(){
              List<String> list = new ArrayList<>();
              list.add("LLLL");
              list.add("AAAA");
              list.add("YYYY");
              list.add("NNNN");
              list.add("IIII");
              System.out.println(list);
              // Collections.sort(list); // 操作的是容器本身
              // System.out.println(list);
      
              // 默认是是升序 上面的排序等价于这个
              Collections.sort(list, Comparator.naturalOrder());
              System.out.println(list);
      
              // 降序
              Collections.sort(list, Comparator.reverseOrder());
              System.out.println(list);
      
          }
      }
  • 随机排序 shuffle(List list)

    import java.util.*;
    
    public class IteratorTest {
        public static void main(String[] args) {
            testShuffle();
        }
    
        public static void testShuffle(){
            List<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            list.add("6");
            list.add("7");
            list.add("8");
            list.add("9");
            list.add("10");
            list.add("J");
            list.add("Q");
            list.add("K");
            System.out.println(list);
    
            Collections.shuffle(list);
            System.out.println(list);
        }
    }
  • 获取最大元素 max(Collection coll) 默认比较,不适合对象比较

  • 获取最大元素 max(Collection coll, Comparator Comparator)

  • 获取最小元素 min(Collection coll)

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class CollectionsTest {
        public static void main(String[] args) {
            List<Student> list = new ArrayList<>();
    
            list.add(new Student("Laobai", 18));
            list.add(new Student("AILynn", 17));
            list.add(new Student("PLScript", 19));
            list.add(new Student("NichengWe", 16));
    
            System.out.println(list);
            Student maxAgeStudent = Collections.max(list, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    return o1.getAge() - o2.getAge();
                }
            });
            System.out.println(maxAgeStudent);
    
            Student minAgeStudent = Collections.min(list, new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    return o1.getAge() - o2.getAge();
                }
            });
            System.out.println(minAgeStudent);
        }
    
    }
    
    class Student{
        private String name;
        private int 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 Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
  • 创建不可变集合 unmodifiablleXXX()

    package test04;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class CollectionsTest {
        public static void main(String[] args) {
    
            List<String> list = new ArrayList<>();
            list.add("Spring");
            list.add("SpringBoot");
            list.add("SpringCloudAlibaba");
    
            // 设置只读集合
            list = Collections.unmodifiableList(list);
    
            list.add("Docker");
        }
    
    }

    上面的代码可以改写为

    public class CollectionsTest {
        public static void main(String[] args) {
            List<String> list;
            list = List.of("Spring", "SpringBoot", "SpringCloudAlibaba");
            list.add("Docker");
        }
    }

Comparable排序接口

Comparable是一个接口,定制排序规则。对实现它的每个类的对象进行整体排序,里面compareTo方法是实现排序的具体方法。比如TreeSetSortedSetCollections。sort()方法调用进行排序;StringInteger等类默认实现了这个接口,所以可以排序。

public interface Comparable<T> {	// T 泛型
  public int compareTo(T o);
}

compareTo方法

  • 用于比较当前对象和指定对象的排序,o为要比较的对象
  • 返回int类型
    • 大于0,表示this大于传过来的对象o,则往后排,即升序;
    • 等于0,表示this等于传过来的对象o
    • 小于0,表示this小于传过来的对象o

被排序的对象类需要实现Comparable接口

import java.util.*;

public class CompareToTest {
    public static void main(String[] args) {
        Set<Student> studentSet = new TreeSet<>();

        studentSet.add(new Student("Laobai", 18));
        studentSet.add(new Student("AILynn", 17));
        studentSet.add(new Student("PLScript", 19));
        studentSet.add(new Student("NichengWe", 16));

        System.out.println(studentSet);

        List<Student> studentList = new ArrayList<>();

        studentList.add(new Student("Laobai", 18));
        studentList.add(new Student("AILynn", 17));
        studentList.add(new Student("PLScript", 19));
        studentList.add(new Student("NichengWe", 16));

        System.out.println(studentList);
        Collections.sort(studentList);
        System.out.println(studentList);
    }
}

class Student implements Comparable<Student>{
    private String name;
    private int 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 Student() {
    }

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

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


    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

执行结果:

[Student{name='NichengWe', age=16}, Student{name='AILynn', age=17}, Student{name='Laobai', age=18}, Student{name='PLScript', age=19}]
[Student{name='Laobai', age=18}, Student{name='AILynn', age=17}, Student{name='PLScript', age=19}, Student{name='NichengWe', age=16}]
[Student{name='NichengWe', age=16}, Student{name='AILynn', age=17}, Student{name='Laobai', age=18}, Student{name='PLScript', age=19}]

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
  目录