shanX
文章31
标签17
分类9
JAVA-集合框架

JAVA-集合框架

集合

什么是集合

概念

​ 对象的容器,实现了对对象常用的操作

和数组的区别

  1. 数组长度固定,集合长度不固定
  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型

位置

​ java.util.*;包

collection 体系

image-20210407105452970

Collection 父接口

特点:代表一组任意类型的对象,无序、无下标、不能重复。

创建集合 Collection collection = new ArrayList();

常用方法

  1. collection.add(Object obj); 添加一个对象;
  2. adddAll(Collection c) ;//将一个集合中的所有对象添加到此集合中;
  3. void clear(); 清空此集合中的所有对象;
  4. contains(Object obj); //检查此集合中是否包含 o 对象;
  5. equals(Object obj); //比较此集合中是否包含 o 对象;
  6. isEmpty(); 判断此集合是否为空;
  7. remove(Object obj); //在此集合中移除 o 对象;
  8. int size(); //返回此集合中的元素个数;
  9. Object[] toArray(); //将此集合转换成数组 ;

遍历元素(重点)

  1. 增强 for(因为无下标)

    or(Object object : collection){ }

  2. 使用迭代器

    //haNext(); 有没有下一个元素
    //next(); 获取下一个元素
    //remove(); 删除当前元素
    Iterator it = collection.iterator();
    while (it.hasNext()) {
        String s = (String) it.next();
        System.out.println(s);
        // 可以使用it.remove(); 进行移除元素
        // collection.remove(); 不能用collection其他方法 会报并发修改异常
    
  3. 列表迭代器

     List list = new ArrayList();
            list.add("aaa");
            list.add("bbb");
            list.add("ccc");
            ListIterator li = list.listIterator();
            while(li.hasNext()){
                System.out.println(li.nextIndex() + ":" + li.next()); //从前往后遍历
            }
    
            while(li.hasPrevious()){
                System.out.println(li.previousIndex() + ":" + li.previous()); //从后往前遍历
            }
    

List 子接口

  1. 特点:有序,有下标,元素可以重复;
  2. 方法:
    1. list.add(int index, Object o) //在 index 位置插入对象 o
    2. list.addAll(int index, Collection c) //将一个集合中的元素添加到此集合中的 index 位置
    3. list.get(int index) //返回集合中指定位置的元素
    4. list.remove(Object o) list.remove(index) //删除集合中指定元素或指定位置的元素
    5. list,isEmpty() //判断是否为空
    6. list.subList(int fromIndex, int toIndex) //返回 fromindex 和 toindex 之间的集合元素,含头不含尾
public static void main(String[] args) {
        List list = new ArrayList<>();

        list.add("apple");
        list.add("xiaomi");
        list.add("huawei");
        System.out.println("元素个数:"+ list.size());
        System.out.println(list.toString());

//        list.remove("xiaomi");
//        System.out.println("元素个数:"+ list.size());
//        list.remove(0);
//        System.out.println("元素个数:"+ list.size());

        System.out.println("----------------for遍历-------------------");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("----------------增强for-------------------");
        for (Object o : list) {
            System.out.println(o);
        }
        System.out.println("----------------使用迭代器进行遍历-------------------");
        Iterator it1 = list.iterator();
        while (it1.hasNext()){
            System.out.println(it1.next());
        }
        System.out.println("----------------使用列表迭代器进行正序遍历-------------------");
        ListIterator it2 = list.listIterator();
        while (it2.hasNext()){
            System.out.println(it2.next());
        }

        //从后向前
        System.out.println("----------------使用列表迭代器进行逆序遍历-------------------");
        while (it2.hasPrevious()){
            System.out.println(it2.previous());
        }

        System.out.println("-----------------------------------------------------------");
        //判断是否存在
        System.out.println(list.contains("xiaomi"));
        //判断是否为空
        System.out.println(list.isEmpty());
    System.out.println("-----------------------------------------------------------");
        //指定位置输出,含头不含尾
        System.out.println(list.subList(1,2));

    }

List 实现类

  1. ArrayLsit [重点]:

    1. 数组结构实现,查询快,增删慢;
    2. JDK1.2 版本,运行效率快,线程不安全。
  2. Vector:

    1. 数组结构实现,查询快,增删慢;
    2. JDK1.0 版本,运行效率慢,线程安全。
  3. LinkedList:

    1. 链表结构实现,增删快,查询慢。

ArrayList 的使用

创建集合 ArrayList arrayList = new ArrayList<>();

  1. 添加元素 arrayList.add();

  2. 删除元素 arrayList.remove(new Student("name", 10));

    这里重写了 equals(this == obj) 方法

 @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if(obj instanceof Student){
            Student s = (Student)obj;
            //4 比较属性
            return this.name.equals(s.getName()) && this.age == s.getAge();
        }
        return false;
    }
  1. 遍历元素【重点】

    1. 迭代器
    Iterator it = arrayList.iterator();
    while(it.hasNext()){
      Student s = (Student)it.next(); //强转
    }
    
    1. 列表迭代器
    ListIterator li = arrayList.listIterator();
    while(li.hasNext()){
      Student s = (Student)li.next(); //从前往后遍历
    }
    
    while(li.hasPrevious()){
      Student s = (Student)li.previous();//从后往前遍历
    }
    
  2. ArrayList 源码分析

    1. private static final int DEFAULT_CAPACITY = 10; //默认容量 10;

      注意:如果没有向集合中添加任何元素时,容量 0,添加一个后,容量为 10;

      每次扩容是原来的 1.5 倍;

    2. private static final Object[] EMPTY_ELEMENTDATA = {}; 存放元素的数组;

    3. private int size; 实际元素个数,一定小于容量;

Vector 类

  1. 创建集合 Vector vector = new Vector<>();
  2. 增加、删除、判断同上与 list 相同
  3. 遍历中枚举器遍历
Enumeration en = vector.elements();
while(en.hasMoreElements()){
  String o = (String)en.nextElement();
  sout(o);
}

LinkedList

  1. ArrayLsit [重点]:

    1. 数组结构实现,查询快,增删慢;
    2. JDK1.2 版本,运行效率快,线程不安全。
  2. Vector:

    1. 数组结构实现,查询快,增删慢;
    2. JDK1.0 版本,运行效率慢,线程安全。
  3. LinkedList:

    1. 链表结构实现,增删快,查询慢。
  4. LinkedList linkedList = new LinkedList();
    
  5. 主要方法与 ArrayList()相同;遍历方法,for, Iterator;

泛型

  1. 本质是参数化类型,把类型作为参数传递
  2. 常见形式有泛型类、泛型接口、泛型方法
  3. 语法 <T, V> T 成为类型占位符,表示一种引用类型,可以写多个逗号隔开
  4. 好处
    1. 提高代码重用性
    2. 防止类型转换异常,提高代码安全性

泛型类

/**
 * 泛型类
 * T是类型占位符,表示一种引用类型,如果编写多个使用逗号隔开
 */
public class MyGeneric<T> {
    T t;
    public void show(T t) {
        System .out.println(t);
    }
    public T getT() {
        return t;
    }
}
        MyGeneric<String> myGeneric = new MyGeneric<String>();
        myGeneric.t = "hello";
        myGeneric.show("大家好,加油");
        String str = myGeneric.getT();
        System.out.println(str);

        MyGeneric<Integer> myGeneric2 = new MyGeneric<>();
        myGeneric2.t = 100;
        myGeneric2.show(200);
        Integer integer = myGeneric2.getT();
        System.out.println(integer);

泛型接口

/**
 * 泛型接口
 * 语法, 接口名<T>
 * 注意,不能泛型静态常量
 */
public interface MyInterface<T> {
    String name = "张三";

    T server(T t);
}
public class MyInterfaceImpl implements MyInterface<String> {

    @Override
    public String server(String t) {
        System.out.println(t);
        return t;
    }
}
public class MyInterfanceImpl2<T> implements MyInterface<T> {

    @Override
    public T server(T t) {
        System.out.println(t);
        return t;
    }
}
        //myinterface
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.server("xxxxxxxxxx");

        //myinterfaceimpl2
        MyInterfanceImpl2<Integer> impl2 = new MyInterfanceImpl2<>();
        impl2.server(1000);
        MyInterfanceImpl2<String> impl3 = new MyInterfanceImpl2<>();
        impl3.server("ccccccccccc");

泛型方法

/**
 * 泛型方法
 * 语法: <T> 返回值类型
 */

public class MyGenericMethod {
    public <T> T show(T t) {
        System.out.println("泛型方法" + t);
        return t;
    }
}
        //泛型方法
        MyGenericMethod myGenericMethod = new MyGenericMethod();
        myGenericMethod.show("bbbb");
        myGenericMethod.show(200);
        myGenericMethod.show(1.23);

泛型集合

概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致

特点:

  1. 编译时即可检查,而非运行时抛出异常
  2. 访问时,不必类型转换(拆箱)
  3. 不同泛型之间应用不能相互赋值,泛型不存在多态
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("xxx");
        arrayList.add("yyy");

        for (String s : arrayList) {
            System.out.println(s.toString());
        }

        ArrayList<Student> arrayList2 = new ArrayList<>();
        Student s1 = new Student("刘德华", 20);
        Student s2 = new Student("郭富城", 21);
        Student s3 = new Student("梁朝伟", 22);
        arrayList2.add(s1);
        arrayList2.add(s2);
        arrayList2.add(s3);

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

Set 集合

  1. 特点:无序,无下标、元素不可重复;
  2. 方法:全部继承自 Collection 中的方法;
  3. 增、删、遍历、判断与 collection 相同;
        Set<String> set = new HashSet<>();
        //1. 添加数据
        set.add("小米");
        set.add("华为");
        set.add("苹果");
        System.out.println("数据个数:" + set.size());
        System.out.println(set.toString());
        //2 删除数据
        //set.remove("小米");
        System.out.println("----------------增强for--------------");
        for (String s : set) {
            System.out.println(s);
        }
        System.out.println("----------------迭代器--------------");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

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

Set 实现类

HashSet[重点]:

  1. 无序,基于 HashCode 实现元素不重复;
  2. 当存入元素的哈希码相同时,会调用 equals 进行确认,如结果为 true,则拒绝后者存入;

存储结构:哈希表(数组+链表+红黑树)

存储过程(重复依据)

  1. 根据 hashCode 计算保存的位置,如果位置为空,直接保存,若不为空,进行第二步

  2. 再执行 equals 方法,如果 equals 为 true,则认为是重复,否则形成链表

    基于 HashCode 计算元素存放位置

    1. 利用 31 这个质数,减少散列冲突

      31 提高执行效率 31 * i = (i << 5) - i 转为移位操作

    2. 当存入元素的哈希码相同时,会调用 equals 进行确认,如果结果为 true,则拒绝后者存入

 public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();

        hashSet.add("刘德华");
        hashSet.add("郭富城");
        hashSet.add("梁朝伟");

        System.out.println("元素个数:" + hashSet.size());
        System.out.println(hashSet.toString());
        //删除元素
//        hashSet.remove("刘德华");
        //增强for
        System.out.println("---------------增强for-----------------");
        for (String s : hashSet) {
            System.out.println(s);
        }

        System.out.println("---------------迭代器-----------------");
        Iterator it= hashSet.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
//重写equals + hashCode
     @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

   @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

TreeSet:

  1. 基于排列顺序实现元素不重复;

  2. 实现了 sortedset 接口,对集合元素自动排序;

  3. 元素对象的类型必须实现,Comparable 接口,指定排序规则;

  4. 通过 CompareTo 方法确定是否为重复元素;

  5. 存储结构:红黑树

  6. 创建集合 TreeSet<String> treeSet = new TreeSet<>()

    添加元素 treeSet.add();

    删除元素 treeSet.remove();

    遍历 1. 增强 for 2. 迭代器

    判断 treeSet.contains();

  7. 补充: TreeSet 集合的使用

    1. Comparator 实现定制比较(比较器)
    2. Comparable 可比较的
    TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    int n1 = o1.getAge() - o2.getAge();
                    int n2 = o1.getName().compareTo(o2.getName());
                    return n1 == 0 ? n2 : n1;
                }
            });
    
  8. TreeSet 案例:

    • 要求使用 TreeSet 集合实现字符串按照长度进行排序;
    • 实现方法:重写 Comparator 的 compare 方法
 /**
     * 要求使用TreeSet 集合实现字符串按照长度进行排序;
     * helloWord  zhang  lisi  wangwu  beijing  xian  nanjing
     * @authoe sx
     */
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int n1 = o1.length()-o2.length();
                int n2 = o1.compareTo(o2);
                return n1==0?n2:n1;
            }
        });
        //添加数据
        treeSet.add("helloWord");
        treeSet.add("zhang");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        treeSet.add("beijing");
        treeSet.add("xian");
        treeSet.add("nanjing");
        System.out.println(treeSet.toString());

    }

[lisi, xian, zhang, wangwu, beijing, nanjing, helloWord]

Map

  1. 特点:存储一对数据(Key, Value),无序、无下标,键不可以重复,值可重复;
  2. 方法:
    1. V put(K key, V value) 将对象存到集合中,关联键值 ;
    2. Object get(Object key) 根据键获得对应的值 ;
    3. Set 返回所有的 Key;
    4. Collection values() 返回包含所有值的 Collection 集合;
    5. Set<Map.Entry<K, V>> 键值匹配的 Set 集合;

Map 接口的使用:

两种遍历方法:

  1. keySet()把 map 中的 key 存在 set 集合中;entrySet()把(key,value)封装成一个 entry 类型(映射对);
  2. entrySet()效率高于 keySet()方法;因为 entrySet 一次性把 key,value 取出。
public static void main(String[] args) {
        //创建Map集合
        Map<String, String> map = new HashMap<>();

        map.put("cn", "中国");
        map.put("uk", "英国");
        map.put("fc", "法国");
        map.put("gm", "德国");

        System.out.println("元素个数:" + map.size());
        System.out.println(map.toString());

        //删除
//        map.remove("fc");
//        System.out.println(map.toString());

        //遍历
        //使用keySet()方法
        System.out.println("----------keySet---------------");
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            System.out.println(key + ":" + map.get(key));
        }
        System.out.println("----------entrySet---------------");
//        使用entrySet方法
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

HashMap[重点]

JDK1.2 版本,线程不安全,运行效率快,允许用 null 作为 key 或是 value。

存储结构:哈希表(数组+链表+红黑树)

使用 key 可使 hashcode 和 equals 作为重复

增、删、遍历、判断与上述一致

HashMap 常用方法:alt + insert 重写 equals 和 hashcode 去掉重复项;

public static void main(String[] args) {
        //创建集合
        HashMap<Student, String> students = new HashMap<>();
        //添加元素
        Student s1 = new Student("孙悟空", 100);
        Student s2 = new Student("猪八戒", 101);
        Student s3 = new Student("沙和尚", 102);
        students.put(s1, "花果山");
        students.put(s2, "黑风洞");
        students.put(s3, "流沙河");
        students.put(s3, "流沙河");
        students.put(new Student("沙和尚", 102), "流沙河");
        System.out.println(students.size());
        System.out.println(students.toString());

        //删除元素
//        students.remove(s1);
//        System.out.println("删除之后:" + students.size());
        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //遍历
        for (Student key : students.keySet()) {
            System.out.println(key.toString() + ":" + students.get(key));
        }
        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //使用entrySet()
        for (Map.Entry<Student, String> studentStringEntry : students.entrySet()) {
            System.out.println(studentStringEntry.getKey() + ":" + studentStringEntry.getValue());
        }

        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //判断
        System.out.println(students.containsKey(s1));
        System.out.println(students.containsValue("流沙河"));
    }

HashMap 源码分析:

  1. static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认初始容量大小

    1 左移 4 位 = 2^4 = 16,底层移位计算最快;

  2. static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量大小;

  3. DEFAULT_LOAD_FACTOR //默认加载因子;

  4. static final int TREEIFY_THRESHOLD = 8;

    static final int MIN_TREEIFY_CAPACITY = 64; //链表长度大于 8,数组长度大于 64 时存储结构改为红黑树;

  5. static final int UNTREEIFY_THRESHOLD = 6; //链表长度小于 6,变回链表结构

源码分析总结

  1. HashMap 刚创建时,table 是 null,节省空间,当添加第一个元素时,table 容量调整为 16
  2. 当元素个数大于阈值 75%(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数
  3. jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64 时,会调整成红黑树,目的是提高效率
  4. jdk1.8 当链表长度 <6 时 调整成链表
  5. jdk1.8 以前,链表是头插入,之后为尾插入

Hashtable

​ JDK1.0 版本,线程安全,运行效率慢,不允许 NULL 作为 key 或是 value。

​ 基本不用。

Properties

​ Hashtable 的子类,需要 key 和 value 都是 String。在流中使用较多,通常用于配置文件的读取。在流的学习中进行使用。

public static void main(String[] args) {
        //创建集合
        Properties properties = new Properties();
        //添加数据
        properties.setProperty("username", "zhangsan");
        properties.setProperty("age", "20");
        System.out.println(properties);

        //遍历
        //keyset
        for (Object o : properties.keySet()) {
            System.out.println(o);
        }

        //entrySet
        for (Map.Entry<Object, Object> objectObjectEntry : properties.entrySet()) {
            System.out.println(objectObjectEntry);
        }

        //stringPropertyName
        for (String stringPropertyName : properties.stringPropertyNames()) {
            System.out.println(stringPropertyName + ":" + properties.getProperty(stringPropertyName));
        }
    }

和流有关的方法

//和流有关的方法
        PrintWriter pw = new PrintWriter("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\print.properties");
        properties.list(pw);
        pw.close();

store 方法

FileOutputStream fos = new FileOutputStream("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\print.properties");
        properties.store(fos, "注释");
        fos.close();

结果:
#\u6CE8\u91CA    properties文件不允许中文
#Sat Apr 10 01:08:21 CST 2021
age=20
username=zhangsan

load 方法

//        load方法
        Properties properties2 = new Properties();
        FileInputStream fis = new FileInputStream("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\\\print.properties");
        properties2.load(fis);
        fis.close();
        System.out.println(properties2.toString());

TreeMap

​ 实现了 SortedMap 接口(是 map 的子接口),可以对 key 自动排序

public static void main(String[] args) {
        TreeMap<Student, String> students = new TreeMap<>();
        Student s1 = new Student("孙悟空", 103);
        Student s2 = new Student("猪八戒", 101);
        Student s3 = new Student("沙和尚", 102);
        students.put(s1, "花果山");
        students.put(s2, "黑风洞");
        students.put(s3, "流沙河");
        students.put(s3, "流沙河");
        students.put(new Student("沙和尚", 102), "流沙河");
        System.out.println(students.size());
        System.out.println(students.toString());
        //删除元素
//        students.remove(s1);
//        System.out.println("删除之后:" + students.size());

        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //遍历
        for (Student key : students.keySet()) {
            System.out.println(key.toString() + ":" + students.get(key));
        }
        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //使用entrySet()
        for (Map.Entry<Student, String> studentStringEntry : students.entrySet()) {
            System.out.println(studentStringEntry.getKey() + ":" + studentStringEntry.getValue());
        }

        System.out.println("+++++++++++++++++++++++++++++++++++++++");
        //判断
        System.out.println(students.containsKey(s1));
        System.out.println(students.containsValue("流沙河"));

    }

定制比较器:重写 compare 方法:

可以自定义规则

TreeMap<Student, String> students = new TreeMap(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int n1 = o1.getAge() - o2.getAge();
                int n2 = o1.getName().compareTo(o2.getName());
                return n1 == 0 ? n2 : n1;
            }
        });

Collection 工具类

​ 概念:集合工具类,定义了除了存取以外的集合常用方法;

​ 1. 排序 Collections.sort()

​ 2. 直接二分查找int i = Collections.binarySearch(list, x); 成功返回索引;

​ 其他方法 :

复制Collections.copy(dest, list);

​ 反转Collections.reverse(list);

​ 打乱ollections.shuffle(list);

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(20);
        list.add(3);
        list.add(14);
        list.add(50);
        list.add(36);
        //排序
        System.out.println("排序之前---" + list.toString());
        Collections.sort(list);
        System.out.println("排序之后---" + list.toString());

        int i = Collections.binarySearch(list, 3);
        System.out.println("第" + i + "位");

        List<Integer> dest = new ArrayList<>();
        //开辟空间,否则dest为空,无法直接copy放入
        for (int j = 0; j < list.size(); j++) {
            dest.add(0);
        }
        Collections.copy(dest, list);
        System.out.println(dest.toString());

        //翻转
        Collections.reverse(list);
        System.out.println(list);

        //打乱
        Collections.shuffle(list);
        System.out.println(list);
    }

​ 补充:

// list转成数组
Integer[] arr = list.toArray(new Integer[10]);
sout(arr.length);
sout(Array.toString(arr));

// 数组转成集合
// 此时为受限集合,不能 添加和删除!
String[] name = {"张三","李四","王五"};
List<String> list2 = Arrays.asList(names);

// 把基本类型数组转为集合时,需要修改为包装类
Integer[] nums = {100, 200, 300, 400, 500};
List<Integer> list3 = Arrays.asList(nums);
本文作者:shanX
本文链接:https://rhymexmove.github.io/2021/04/10/240e3566888e/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可