Java基础第二遍-15-集合

集合

image-20211113235353863

image-20211113235432136

为什么出现集合类?

  1. 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式

数组和集合都是容器,有何不同

  1. 数组虽然也可以存储对象,但长度是固定的,集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象
  2. 集合不可以存储基本数据类型值

集合类的特点

  1. 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象

Collection接口

  1. 集合容器因为数据结构的不同,有多个具体的容器类型,对他们进行共性抽取,形成了集合框架Collection接口,他具备容器的基本操作

  2. 基本操作:

    1. add(Object obj);
    2. addAll(Collection c);
    3. remove(Object o);
    4. removeAll(Collection c);//将两集合的相同元素,从调用者的集合中删除
    5. clear();
    6. contains(Object o);
    7. containsAll(Collection c);
    8. size();
    9. Iterator iterator();//迭代器
    10. retainAll(Collection c);//取两集合的交集,调用者集合中保留两集合的交集,其他的删除
    11. toArray();//将集合转为数组
  3. package com.bixiangdong.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class Demo01 {
        public static void main(String[] args) {
            //Collection迭代器
            Collection coll = new ArrayList();
            for (int i = 0; i < 10; i++) {
                coll.add("test"+i);
            }
            System.out.println(coll);
            //使用迭代器
            //Iterator it = coll.iterator();
            //System.out.println(it.next());//返回第一个元素,连续使用默认指针向后移动
            //循环,it会始终存在
    //        while (it.hasNext()){
    //            System.out.println(it.next());
    //        }
            //for循环it只在for中存在,循环结束it销毁  it:迭代器对象
            for (Iterator it = coll.iterator(); it.hasNext();) {
                System.out.println(it.next());
            }
        }
    }
    
    
  4. 集合框架-迭代器图解

    每个具体的容器,都有自己的取出方式,Collection接口的迭代器也是一个接口,具体的容器需要对其进行实现,每个容器都有公共方法hasNext();next();,可以对容器进行操作,所以只需要获取Iterator对象就可以实现元素获取

    image-20211113173755668

list集合

  1. 有序:存入和取出的顺序一致(对存储的元素位置进行精确控制,索引下标)

  2. 允许出现重复元素

  3. list特有的方法

    1. 都可以操作角标(索引)
    2. add(index,element);
    3. add(index,collection);
    4. remove(index);
    5. set(index,element);//根据角标,修改元素
    6. get(index);
    7. indexOf(object);//从第一个元素查找某个元素,并返回其下标
    8. lastIndexOf(object);//从最后一个元素开始查找某个元素 并返回其下标
    9. subList(from,to);//取出两角标间元素
  4. list的特有迭代器(只有list集合具有):listIterator,可以对集合实现,增删改查(collection中的Iterator不足以满足对集合操作的需求)

  5. list接口的常用集合类:ArrayList、LinkedList、Vector

Vector

  1. 内部是数组数据结构,数组长度可变(实现原理,创建一个新的数组将原来数组的值赋给新的数组),元素都具有索引
  2. 是同步的。线程安全
  3. 查询增删都慢

ArrayList

  1. 内部是数组数据结构,数组长度可变(实现原理,创建一个新的数组将原来数组的值赋给新的数组),元素都具有索引
  2. 不同步,线程不安全,代替了Vector
  3. 查询快(内存空间连续),增删慢
  4. contains:方法仅依赖equals()方法

LinkedList

  1. 内部是链表数据结构,增删元素的速度很快只影响要操作位置的前后两个元素)数组结构的话牵一发而动全身(对某一个位置元素进行增删,其后的所有元素都需要改变位置

    image-20211113181558083

  2. 不同步的

  3. 增删速度快,查询慢(内存空间不连续)

  4. 特有方法:

    1. addFirst();
    2. addLast();
    3. getFirst();//获取但不移除元素,如果link为空,抛异常
    4. getLast();
    5. removeFirst();//获取并移除元素,如果link为空,抛异常
    6. removeLast();
    7. isEmpty();
    8. peekFirst();//获取但不移除元素,如果link为空,返回null
    9. pollFirst();//获取并移除元素,如果link为空,返回null

堆栈

  1. 栈:先进后出(像弹夹)

队列

  1. 先进先出(像管子)

模拟栈式结构

package com.bixiangdong.collection;

import java.util.LinkedList;

public class Demo03 {
    public static void main(String[] args) {
        Que que = new Que();
        que.add("a1");
        que.add("a2");
        que.add("a3");
        que.add("a4");
        que.add("a5");

        while (!que.isNull()){
            //先进后出
            System.out.println(que.get());
        }
    }
}
//模拟栈式存储 先进后出
class Que{
    private LinkedList list;
    public Que(){
        list=new LinkedList();
    }
    //添加
    public void add(Object o){
        this.list.addFirst(o);
    }
    //获取
    public Object get(){
       return this.list.removeFirst();
    }
    //判断是否为空
    public Boolean isNull(){
        return this.list.isEmpty();
    }
}

set集合

  1. 无序存的顺序与取的顺序不一致

  2. 元素不能重复(元素的存储位置是算出来的,所以不能重复)

HashSet

  1. 内部数据结构是Hash表

  2. 不同步

    Hash表存储图解:

    image-20211113192229297

    1. Hash表确定元素是否相同
      1. 判断的是两个元素的哈希值是否相同
        1. 如果相同,再判断内容是否相同
      2. 判断Hash值是否相同,其实判断的是对象的hashCode方法,判断内容相同判断的是equals方法
      3. 注意:Hash值不同,不用判断内容是否相同

    Hash冲突

    1. Hash地址相同,内容不同(会在同一个地址上进行多个内容存储,有另外一套存储逻辑)

    自定义HashCode与equals

    package com.bixiangdong.collection;
    
    import java.util.HashSet;
    import java.util.Iterator;
    
    public class Demo04 {
        public static void main(String[] args) {
            HashSet hashSet = new HashSet();
            hashSet.add(new Person("a",12));
            hashSet.add(new Person("b",12));
            hashSet.add(new Person("c",13));
            hashSet.add(new Person("a",12));
    
            Iterator iterator = hashSet.iterator();
            while (iterator.hasNext()){
                Person next = (Person)iterator.next();
                System.out.println(next.toString());
            }
            /*  结果:
            *   Person{name='a', age=12}
                Person{name='b', age=12}
                Person{name='c', age=13}
            * */
        }
    }
    
    class Person{
        private String name;
        private int age;
    
        @Override
        public int hashCode() {
            //因为每个类都继承了Object的hashCode方法 ,创建对象的时候,都具有唯一的内存地址
            //但是在使用hashSet进行存储的时候,会出现存储同一个内容的不同对象的情况,
            //所以可以使用重写hashcode的方式,进行自定义的实现
            //自定义hashCode 算出内存地址
            return this.name.hashCode()+age*23;
        }
    
        @Override
        public boolean equals(Object obj) {
            //如果hashCode的值相同的话,会比对内容,所以也需要重写
            Person p = (Person)obj;
            return this.name.equals(p.name)&&this.age==p.age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        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;
        }
    }
    
  3. 操作元素判断:hashCode和equals

LinkedHashSet

  1. 有序的
  2. HashSet的子类

TreeSet

1. HashSet的子类

2. **可以对集合中的元素 进行自然排序(需要实现Comparable重写compareTo方法),返回为0默认对象相同,为负数则对象小于当前对象,为整数则对象大于当前对象** 不是使用hashCode和equals进行比较

3. 倘若不能在类中重写compareTo方法,获取类具有的compareTo方法不符合当前的情况,那么就让TreeSet具有排序功能(**给TreeSet创建一个比较器**)

4. **不同步的**

5. 类重写compareTo

6. ```java
   package com.bixiangdong.collection;


import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
// 类重写compareTo
public class Demo05 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Person1("afsd",12));
treeSet.add(new Person1("gfsd",13));
treeSet.add(new Person1("hfsd",14));
treeSet.add(new Person1("dfsd",15));
treeSet.add(new Person1("qfsd",13));
treeSet.add(new Person1("jfsd",19));

           Iterator iterator = treeSet.iterator();
           while (iterator.hasNext()){
               Person1 p = (Person1) iterator.next();
               System.out.println(p.toString());
           }
       }
   }
   
   class Person1 implements Comparable{
       private String name;
       private int age;
   
       @Override
       public String toString() {
           return "Person1{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
       }
   
       public Person1(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(Object o) {
           //先按照年龄大小排序,如果年龄相同 则按照姓名排序
           Person1 p = (Person1)o;
           int temp = this.age-p.age;
           return temp==0?this.name.hashCode()-p.name.hashCode():temp;
       }
   }
   ```

7. 给TreeSet创建比较器

8. ```java
   package com.bixiangdong.collection;
   
   import java.util.Comparator;
   import java.util.Iterator;
   import java.util.TreeSet;
   
   public class Demo06 {
       public static void main(String[] args) {
           TreeSet treeSet = new TreeSet(new myComparetor());
           treeSet.add(new Person2("afsd",12));
           treeSet.add(new Person2("gfsd",13));
           treeSet.add(new Person2("hfsd",14));
           treeSet.add(new Person2("dfsd",15));
           treeSet.add(new Person2("qfsd",13));
           treeSet.add(new Person2("jfsd",19));
   
           Iterator iterator = treeSet.iterator();
           while (iterator.hasNext()){
               Person2 p = (Person2) iterator.next();
               System.out.println(p.toString());
           }
       }
   }
   
   class Person2{
       private String name;
       private int age;
   
       @Override
       public String toString() {
           return "Person2{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
       }
   
       public Person2(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;
       }
   
   }
   // 自定义比较器
   class myComparetor implements Comparator{
   
       @Override
       public int compare(Object o1, Object o2) {
           Person2 p1 = (Person2)o1;
           Person2 p2 = (Person2)o2;
           int temp = p1.getAge()- p2.getAge();
           return temp==0?(p1.getName().hashCode()-p2.getName().hashCode()):temp;
       }
   }
   
   ```

9. ![image-20211113212816914](https://gitee.com/gaopeng111/picture-library/raw/master/D:%5C%E7%AC%94%E8%AE%B0/image-20211113212816914.png)

   相当于后面存储的 都默认比上一个大(或者小)。

Map接口

  1. 双列集合

  2. 键值对

  3. 键唯一

  4. 无序的(存储顺序与取出顺序不同)

  5. 常用方法

    1. put(key,value);//返回上一个和key关联的value,没有则返回null
    2. clear();//清空
    3. remove(key);//根据key删除
    4. containsKey(key);//是否包含key
    5. containsValue(value);//是否包含value
    6. isEmpty();//是否为空
    7. get(key);//根据key获取value 如果有返回value,没有则返回null
    8. size();//键值对的个数
    9. keySet();//获取所有的键,返回一个Set集合(key不能重复,所以是Set集合) 通过这个可以遍历Map
  6. 使用entrySet遍历Map

    package com.bixiangdong.collection;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    public class Demo07 {
        public static void main(String[] args) {
            Map map = new HashMap();
            map.put(1,"fdsa");
            map.put(2,"fdsa");
            map.put(3,"fdsa");
            map.put(4,"fdsa");
            map.put(5,"fdsa");
            Set set = map.entrySet();
            Iterator iterator = set.iterator();
            while (iterator.hasNext()){
                Map.Entry next = (Map.Entry)iterator.next();
    
                System.out.print(next.getKey()+"...");
                System.out.println(next.getValue());
            }
        }
    }
    
    

HashMap

  1. 内部数据结构是Hash表
  2. 不同步
  3. 允许null作为键,null作为值

Hashtable

  1. 内部数据结构是Hash表
  2. 是同步的
  3. 不允许null作为键,null作为值

TreeMap

  1. 内部数据结构二叉树
  2. 不同步
  3. 可以对Map集合中的进行排序
  4. 也可以使用比较器设定排序TreeMap map = new TreeMap(new MyComparetor());

泛型

  1. 约束(ArrayList<String> list = new ArrayList<String>();

  2. 安全,类型转换的时候类型一致,不会出现问题(避免强转的麻烦)

  3. 规范,数据类型一致,便于开发维护

  4. 简化书写,提高开发效率

  5. 将运行时期的问题ClassCastException转到了编译时期

  6. 什么时候使用 泛型:对引用的数据类型有需求的时候

  7. 泛型擦除:泛型用于编译时期,在运行时会将泛型去掉,生成class文件中是没有泛型的,

  8. 为什么擦除?为了兼容运行的类加载器

  9. 泛型的补偿机制:在运行时通过获取元素的类型进行转换动作(通过反射机制:getClass().getName()获取类型),不用使用者再进行强转了

  10. 自定义泛型类

    //自定义泛型类
    class Tool<T>{
        private T t;
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
    }
    
  11. 将泛型定义在方法上

    class Tool<T>{
        private T t;
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
        
        public <W> void show(W w){
            System.out.println(w.toString()+"将泛型定义在方法上");
        }
    }
    
    
  12. 方法静态时,泛型只能定义在方法上(返回值类型前,修饰符后)

    class Tool<T>{
        private T t;
    
        public T getT() {
            return t;
        }
    
        public void setT(T t) {
            this.t = t;
        }
    
        public <W> void show(W w){
            System.out.println(w.toString()+"将泛型定义在方法上");
        }
    
        public static <W> void show2(W w){
            System.out.println(w.toString()+"将泛型定义在方法上");
        }
    }
    
  13. 实现类实现接口时,若接口有泛型,需要定义,如果不知道定义哪种类型,需要实现类的泛型与接口泛型保持一致

    interface Tool2<T>{
        void show(T t);
    }
    //T Q是形参 可以命名不一致
    class T2<Q> implements Tool2<Q>{
        @Override
        public void show(Q q) {
            System.out.println(q.toString());
        }
    }
    
  14. 泛型的通配符:?

    ?表示未知类型

    class Tool3{
        //不明确返回值类型时可以使用?
        void show(Collection<?> c){
            Iterator<?> iterator = c.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
        //明确返回值类型时,需要使用字母 
        <X> X show2(Collection<X> c) {
            Iterator<?> iterator = c.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
            return null;
        }
    }
    
  15. 泛型通配符的使用---指定某个类型的子类或父类(泛型的限定)

    class Tool4{
        //接收Person类或者其子类
        <X> X show2(Collection<? extends Person> c) {
            Iterator<? extends Person> iterator = c.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
            return null;
        }
        //接收Person类或者其父类
        <X> X show3(Collection<? super Person> c) {
            Iterator<? super Person> iterator = c.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
            return null;
        }
    }
    
  16. 一般存元素的时候都是使用上限(? extends E),因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患

  17. 对集合中的元素进行取出操作时,可以使用下限(?super E)

集合的使用技巧

  1. 可以重复吗

    1. 使用List
      1. 需要频繁增删吗
      2. 需要
        1. LinkedList
      3. 不需要
        1. ArrayList
  2. 不能

    1. 使用set
    2. 需要制定顺序吗
      1. 需要
        1. TreeSet
      2. 不需要
        1. HashSet
    3. 想要一个和存储一致的顺序:LinkedHashSet
  3. 看到array:想到数组,查询快,有角标

  4. 看到link:想到链表,增删快,头和尾first、last

  5. 看到hash:想到哈希表,唯一性,元素要覆盖hashCode和equals方法

  6. 看到tree:想到二叉树,可排序,比较接口Comparable、Comparator

posted @ 2021-11-13 22:21  争取做百分之一  阅读(32)  评论(0)    收藏  举报