Java集合(容器)

集合的使用

主要内容

  • 集合总体结构介绍
  • List 实现类
  • Set的实现类
  • Map实现类
  • Iterator 迭代器
  • Collections 集合工具类

学习目标

知识点要求
集合总体结构介绍掌握
List实现类掌握
Set的实现类掌握
Map实现类掌握
Iterator掌握
Collections掌握

一. 集合介绍

1. 介绍

​ 集合又称容器。是Java中对数据结构(数据存储方式)的具体实现。

​ 我们可以利用集合存放数据,也可以对集合进行新增、删除、修改、查看等操作。

​ 集合中数据都是在内存中,当程序关闭或重启后集合中数据会丢失。所以集合是一种临时存储数据的容器。

2. JDK中集合结构图(常见面试题)

​ 集合作为一个容器,可以存储多个元素,但是由于数据结构的不同,java提供了多种集合类。将集合类中共性的功能,不断向上抽取,最终形成了集合体系结构。

image-20220321191747401

常用实现类:ArrayList, LinkedList, HashSet(Linked记录顺序), HashMap(Linked记录顺序)

2.1 List接口和Set接口

List和Set的父接口

​ 1. List接口:存储有序, 可重复数据。

​ 1. Vector:List的实现类,底层为可变长度数组实现。所有方法都是同步操作(线程安全),每次扩容成本增长。新数组长度为原数组长度的2倍

​ 2. ArrayList:List的实现类,底层为可变长度数组实现。所有方法都是非同步操作(非线程安全的),以1.5倍的方式在扩容。常用于: 查询较多的情况

​ 3. LinkedList:List的实现类,双向非循环链表的实现。常用于: 删 增 较多的情况

2.Set接口:存储无序,不可重复数据。

​ 1. HashSet:Set实现类,底层是HashMap 散列表(数组+链表+(红黑树 jdk1.8及之后))。所有添加到 HashSet 中的元素实际存储到了HashMap的key中

​ 2. LinkedHashSet:HashSet子类. 使用LinkedHashMap来存储它的元素,存储的值插入到LinkedHashMap的可以key中, 底层实现(数组+链表+(红黑树 jdk1.8及之后) + 链表), 可记录插入的顺序

​ 3. TreeSet:Set实现类,底层是TreeMap(红黑树实现), 存入到TreeSet中的元素, 实际存储到了TreeMap中, 根据存储元素的大小可以进行排序

2.2 Map接口

Map:独立的接口,映射。每个元素都包含Key(名称)和Value(要存储的值)两个值。

​ 1. HashMap:Map实现类, 对散列表 (数组+链表+(红黑树Java8及之后))的具体实现,非同步操作(非线程安全的)。存储时以Entry类型存储(key, value)

​ 2. LinkedHashMap: HashMap的子类,是基于HashMap和链表来实现的。在hashMap存储结构之上再添加链表, 链表只是为了保证顺序

​ 3. TreeMap:Map实现类, 使用的不是散列表, 而是对红黑树的具体实现。根据key值的大小, 放入红黑树中, 可实现排序的功能(key值大小的排序)

​ 4. HashTable:Map实现类, 和HashMap数据结构一样,采用散列表(数组+链表+(红黑树 jdk1.8及之后))的方法实现, 对外提供的public函数几乎都是同步的(线程安全)。

常用的集合: ArrayList, HashMap, HashSet

二. Collection接口

**1. **介绍

​ List和Set接口的父接口, 还有其他的实现类或子接口。

2. 继承关系

3. 包含的API

三. List接口

**1. **介绍

​ Collection接口的子接口。Collection中包含的内容List接口中可以继承。

​ List专门存储有序,可重复数据的接口。

2. 包含的API

四. ArrayList

**1. ** 介绍

​ 实现了List接口, 底层实现可变长度数组。存储有序、可重复数据, 有下标。

**2. ** 实例化

​ 常用向上转型进行实例化。绝大多数集合都支持泛型,如果不写泛型认为泛型是,使用集合时建议一定要指定泛型。

​ List<泛型类型> 对象 = new ArrayList<>();

**3. ** 内存结构图

4. 常用API

public class TestArrayList {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        
        list.add("zwx");//添加到尾部
        list.add(0,"htt");//添加到指定的位置

        
        list.remove("zwx");//根据指定元素删除
        list.remove(0);//根据指定下标删除

        
        list.set(0,"aaa");

        
        //遍历
        for (String s : list) {//list可以看出是String[]
            System.out.println(s);
        }
        //遍历
        for (int i = 0; i <list.size(); i++) {
            System.out.println(list.get(i));//得到list下标i对应的元素
        }

        //判断
        int inx = list.indexOf("wx");//元素存在, 返回元素的下标. 不存在, 返回-1。虽然有zwx,但并没有wx,返回false
        boolean bool = list.contains("wx");//元素存在, 返回true. 不存在, 返回false。虽然有zwx,但并没有wx,返回false

        //查看元素个数
        System.out.println(list.size());

        //得到元素(根据下标)
        System.out.println(list.get(1));
        

    }
}

remove()方法强调

五. 泛型为集合类型

1. 介绍

​ 在集合中泛型都是任意引用类型。既然是任意引用类型,也可以是集合类型。

2. 实例化语法

//例如:
List<List<Integer>> list = new ArrayList<>();

3. 内存结构图

4. 代码示例

public class Test {
  public static void main(String[] args) {
        List<List<Integer>> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>();

        list2.add(1);
        list2.add(2);
        list2.add(3);

        list3.add(11);
        list3.add(22);
        list3.add(33);
		
      	//把list2,list3加到list1
        list1.add(list2);
        list1.add(list3);

        for (List<Integer> itgList : list1) {//list1可以看成是List<Integer>[]类型
            for (int i : itgList) {//itgList可以看成是int[]类型或Integer[]类型,故前面可以写成int i或Integer i
                System.out.print(i + " ");
            }
        }

  }
}

5.<? extends E>

泛型中<? extends E> 代表:集合只要是E类型或E类型的子类都可以。表示上界是E,? 继承 People

表示集合中的元素上限是People,即只能是People或People的子类,所以下面的赋值是合法的,编译时候不会报错:

img

但不能是其父类,否则编译时候就报错了:

img

6.<? super E>

泛型中<? super E> 代表:集合只要是E类型或E类型的父类都可以。表示下界是Man,?超越Man

该表示给出了集合中元素的下限是Man,即只能为Man或者Man的父类,而不能是Man的子类,如下:

img

六. LinkedList

1. 介绍

​ LinkedList是Java中对双向非循环链表的实现。实现了List接口。

​ 具有ArrayList所有常用方法,额外还添加了头尾操作方法(实现了Deque接口),这些方法在List接口中是不存在的,所以如果希望使用这些头尾操作方法,实例化时不用向上转型。

故一般使用LinkedList时不向上转型

2. 实例化语法

LinkedList<泛型> 对象 = new LinkedList<>();

3. 常用API

​ ArrayList里面常用API在LinkedList中都可以使用。

​ 下面的演示为LinkedList比ArrayList多的常用方法。

public class TestLinkedList {
  public static void main(String[] args) {
     LinkedList<String> linkedList = new LinkedList<>();
	
        //增
        //在头结点新加
        linkedList.addFirst("aa");
        linkedList.addFirst("bb");
        //在尾结点新加
        linkedList.addLast("cc");
        linkedList.addLast("dd");

        //删
        //删头结点,没有头结点则java.util.NoSuchElementException
        linkedList.removeFirst();
        //删头结点,没有头结点则返回null
        linkedList.pollFirst();

        //String s1 = linkedList.removeFirst(); //返回被删除的元素
        //System.out.println("本次删除的是:" + s1);       
                
        //删尾结点,没有尾结点则java.util.NoSuchElementException
        linkedList.removeLast();
        //删尾结点,没有尾结点则返回null
        linkedList.pollLast();
        
        //查
        //获取头结点 没有则 java.util.NoSuchElementException
        String first1 = linkedList.getFirst();
        //获取头结点 没有则返回null
        String first2 = linkedList.peekFirst();

        //获取尾结点
        String last1 = linkedList.getLast();
        //获取尾结点
        String last2 = linkedList.peekLast();

        //遍历
//        for (String str:linkedList) {	//linkedList可以看成是String[]类型
//            System.out.println(str);
//        }

  }
}

**问题1:**将ArrayList替换成LinkedList之后,变化的是什么?

底层的结构变了

ArrayList:数组 LinkedList:双向非循环链表

问题2:到底是使用ArrayList还是LinkedList

根据使用场合而定

大量的根据索引查询的操作,大量的遍历操作(按照索引0–n-1逐个查询一般),建议使用ArrayList

如果存在较多的添加、删除操作,建议使用LinkedList

问题3:LinkedList增加了哪些方法

增加了对添加、删除、获取(增,删,查)首尾元素的方法

addFirst()、addLast()、removeFirst()、removeLast()、getFirst()、getLast()

七、Java中栈和队列的实现类

Vector过时了,被ArrayList替代了,Stack也就过时了

public  class Stack<E> extends Vector<E>  

Deque和Queue的实现类,用的非常少了解即可
1.ArrayDeque 顺序栈 数组
2.LinkedList 链栈 链表

 public interface Queue<E> extends Collection<E>
 public interface Deque<E> extends Queue<E>

1. 早期的栈结构实现类 Stack

public class Test1 {
 public static void main(String[] args) {
        //创建栈对象
        Stack<String> stack = new Stack<>();

        //入栈,先进后出
        stack.push("马云");
        stack.push("马化腾");
        stack.push("李彦宏");
        stack.push("马老师");
        System.out.println(stack);//输出栈

        //弹出栈顶元素
        String pop = stack.pop();
        System.out.println("弹出" + pop);

        System.out.println(stack);//输出栈
    }
}

2.Queue单端队列

public class Test2 {
    public static void main(String[] args) {
        Queue<String> q=new LinkedList<String>();
        // 入队,先进先出
        q.offer("张三丰");
        q.offer("张翠山");
        q.offer("张无忌");

        System.out.println(q);

        // 出队 取出队首
        String poll = q.poll();
        
        System.out.println(poll);
        System.out.println(q);

    }
}

3.Deque双端队列

public class TestLinkedList2 {
    public static void main(String[] args) { 
         //创建Deque双端队列对象
        Deque<String> dq = new LinkedList<>();

        //入队
        dq.offerFirst("盘子1");//队首入队

        dq.offerLast("盘子2");//队尾入队


        //出队
        dq.pollFirst();//队首出队

        dq.pollLast();//队尾出队

        
        System.out.println(dq.size());//输出队列中有多少个元素
        
  
    }
}

八. Set接口

**1. **介绍

​ Set继承了Collection接口。继承的都是Collection中的方法, 没有提供额外方法。

​ Set经常称为实现无序, 不重复数据集合, 指的就是HashSet实现类

2. 包含API

九. HashSet

1. 介绍

​ 完全基于HashMap(数组+链表+(红黑树))实现的。

​ 存储无序, 无下标, 元素不重复数据。

2. 代码示例

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

        ArrayList<Integer> list = new ArrayList<>();
        //创建HashSet对象
        Set<Integer> set = new HashSet<>();


        
        set.add(11);
        set.add(22);
        set.add(33);
        //将其他集合中的元素添加到set集合中
        list.add(3);list.add(4);list.add(4);
        set.addAll(list);
        
        
        //删除指定元素11
        set.remove(11);
  ;

        
        for (Integer integer : set) {
            System.out.print(integer + " ");
        }
        //也可以直接用sout输出
        System.out.println(set);


        //是否包含指定元素
        System.out.println(set.contains(22));


        //元素个数
        System.out.println(set.size());
    }
}

十. TreeSet

**1. **介绍

​ 底层是基于TreeMap红黑树。

public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("zwx");list.add("htt");


        //创建TreeSet对象
        Set<String> treeSet = new TreeSet<>();

        
        //添加元素
        treeSet.add("abc");
        treeSet.add("abc");//不能添加相同的元素
        treeSet.add("fgh");
        treeSet.add("jkl");
        System.out.println(treeSet);
        //将其他集合中元素添加到set集合中
        treeSet.addAll(list);
        System.out.println(treeSet);

        
        //删除指定元素“abc”
        treeSet.remove("abc");
        System.out.println(treeSet);

        
        for (String str:
             treeSet) {
            System.out.println(str);
        }

        //是否包含指定元素
        System.out.println(treeSet.contains("abc"));

        //元素个数
        System.out.println(treeSet.size());

    }

2.使用Set集合分别存储学生对象

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

        //创建一个集合对象
        Set<Student> set1 = new HashSet<Student>();
        Set<Student> set2 = new LinkedHashSet<Student>();
        Set<Student> set3 = new TreeSet<Student>();//


		//测试,set1,set2都运行没问题,set3直接异常无法运行
        set1.add(new Student(2,"zwx",24,99));
        set1.add(new Student(3,"htt",23,89));
        set1.add(new Student(1,"smh",23,100));
        set1.add(new Student(1,"smh",23,100));

        System.out.println(set1.size());//set1,set2都4个,set3直接异常无法运行
        System.out.println(set1);
    }
}

问题1:HashSet、LinkedHashSet :为什么String有重复,会保持唯一;为什么Student有重复,不会保持唯一。

解答1:HashSet、LinkedHashSet 需要Student实现hashCode()和equals()

问题2:TreeSet 为什么String可以添加,而Student就不让添加到TreeSet中呢? 而是抛出异常:

java.lang.ClassCastException: com.bjsxt.entity.Student cannot be cast to java.lang.Comparable

思考:String是系统类,Student是自定义类,应该是String已经做了某些事情,但是Student没有做

解答2:TreeSet 需要Student实现Comparable接口并指定比较的规则

3.重写Student的equals()和hashCode()方法

public class Student implements Comparable<Student>{
    private int sno;
    private String name;
    private int age;
    private double score; 
    @Override
    public int compareTo(Student o) {
        //return this.sno - o.sno;
        //return o.sno - this.sno;
        return -(this.sno - o.sno);
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (sno != student.sno) return false;
        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return name != null ? name.equals(student.name) : 
student.name == null;
    }
    @Override
    public int hashCode() {
        int result;
        long temp;
        result = sno;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

4.比较器Comparator的作用和使用

import java.util.Objects;

public class Student implements Comparable<Student> {

     private  int  age;
     private  String name;
     private  String sex;

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

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

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

十一. Map接口

**1. **接口

​ Map是独立的接口。和Collection没有关系。

​ Map中每个元素都是Entry类型,每个元素都包含Key(键)和Value(值)

2. 继承关系

3. 包含的API

十二. HashMap

**1. **介绍

​ HashMap是对散列表的具体实现。

​ 里面都包含Key-Value值。

2. 代码演示

public class TestHashMap {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();

        
        map.put("aa",11);
        map.put("bb",22);
        map.put("cc",33);
        map.put("cc",33);//不会重复增加
        map.put(null,44);

        
        //根据key  删除键值对  返回被删除的值
        int delete = map.remove("cc");


        
        //获取存储键值个数
        map.size();

        //是否包含指定的key
        map.containsKey("bb");

        //是否包含指定的值
        map.containsValue(11);

        //根据key获取value
        map.get("bb");
        
        ///遍历
        //键遍历
        Set<String> strings = map.keySet();
        for (String key : strings) {//快捷键key.for
            System.out.println("key->" + key + " value->" + map.get(key));
        }

        //值遍历
        Collection<Integer> values = map.values();
        for (Integer value : values) {//快捷键value.for
            System.out.println(value);
        }

        //键值遍历
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

    }
}

十三. TreeMap

1. 简介

​ 红黑树的具体实现。

2. 代码示例

​ 总体和HashMap使用非常类型

public class TestTreeMap {
  public static void main(String[] args) {
    Map<Integer, String> treeMap = new TreeMap<>();
    treeMap.put(2, "bb");
    treeMap.put(1, "aa");
    treeMap.put(3, "cc");
    for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
      System.out.println(entry.getKey() + ":" + entry.getValue());
    }
  }
}

十四. Iterator

1. 简介

​ 中文名称:迭代器。是一个接口,每个集合中实现类都对Iterator提供了内部类的实现。

​ 通过Iterator可以实现遍历集合的效果。

​ 存在意义:

​ 隐藏集合实现细节,无论是哪种集合都是通过Iterator进行操作,而不是直接操作集合。通过一套API实现所有集合的遍历。

​ 可以在遍历时删除集合中的值。

2. 实例化

​ 每个实现类都提供了.iterator();返回值就是迭代器对象。

public class TestIterator {
  public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //获取集合的迭代器
    Iterator<Integer> iterator = list.iterator();
    //.hasNext() 判断是否有下一个元素
    while (iterator.hasNext()) {
      //获取下一个元素
      Integer next = iterator.next();
      System.out.println(next);
    }
  }
}

3. ConcurrentModificationException

​ 在循环遍历集合时,向集合中插入值或删除集合中值时会出现这个异常。

public class Test {
  public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    for (Integer integer : list) {
      list.remove(integer);
    }
  }
}

​ 异常截图:

为什么使用for(int i =0;i<list.size();i++){}时不出现这个异常

​ 因为这种循环其实是多次执行get,调用get()方法时,集合其他元素删除或新增是没有要求的。而增强for循环是把集合看做一个整体,在遍历集合时,不允许对整个集合进行操作。

4. 遍历集合时删除元素内容

public class Test {
  public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //获取集合的迭代器
    Iterator<Integer> iterator = list.iterator();
    //.hasNext() 判断是否有下一个元素
    while (iterator.hasNext()) {
      //获取下一个元素
      Integer next = iterator.next();
      //删除当前元素
      iterator.remove();
    }
  }
}

总结:

  1. Iterator专门为遍历集合而生,集合并没有提供专门的遍历的方法

​ Iterator实际上迭代器设计模式的实现

  1. 哪些集合可以使用Iterator遍历

    层次1:Collection、List、Set可以、Map不可以

    层次2:提供iterator()方法的就可以将元素交给Iterator;

​ 层次3:实现Iterable接口的集合类都可以使用迭代器遍历

  1. for-each循环和Iterator的联系

    for-each循环(遍历集合)时,底层使用的是Iterator

  2. for-each循环和Iterator的区别

​ for-each还能遍历数组,Iterator只能遍历集合

​ 使用for-each遍历集合时不能删除元素,会抛出异常ConcurrentModificationException使用Iterator遍历合时能删除元素

5.ListIterator

ListIterator和Iterator的关系

  1. ​ public interface ListIterator extends Iterator
  2. ​ 都可以遍历List

ListIterator和Iterator的区别

  1. ​ 使用范围不同
  2. ​ Iterator可以应用于更多的集合,Set、List和这些集合的子类型。
  3. ​ ListIterator只能用于List及其子类型。
public class TestListIterator {
    public static void main(String[] args) {
        //创建一个集合对象
        List<Integer> list = new ArrayList<Integer>();
        //向集合中添加分数
        list.add(78);
        list.add(80);
        list.add(89);        
        ListIterator<Integer> lit = list.listIterator();        
        while(lit.hasNext()){
            lit.next();
        }        
        while(lit.hasPrevious()){
            int elem = lit.previous();
            System.out.println(elem +"  "+lit.nextIndex()+"  "+lit.previousIndex());
        }
    }
}

十五. Collections

1. 介绍

​ Collections是一个工具类型,一个专门操作集合的工具类。

2. 代码示例

public class TestCollections {
    public static void main(String[] args) {
        //添加元素
        List<Integer> list = new ArrayList();
        Collections.addAll(list, 10, 50, 30, 90, 85, 100);//6
        System.out.println(list);
        //排序
        Collections.sort(list);//默认按照内部比较器
        System.out.println(list);
        //查找元素(元素必须有序)
        int index = Collections.binarySearch(list, 500);//不存在返回负数
        System.out.println(index);
        //获取最大值和最小值
        int max = Collections.max(list);
        int min = Collections.min(list);
        System.out.println(max + "   " + min);
        //填充集合
        Collections.fill(list, null);
        System.out.println(list);
        //复制集合
        List list2 = new ArrayList();
        Collections.addAll(list2, 10, 20, 30, 50);
        System.out.println(list2);
        Collections.copy(list, list2);//dest.size >= src.size  目标列表的长度至少必须等于源列表。
        System.out.println(list);
        //同步集合
        //StringBuffer 线程安全效率低 StringBuilder 线程不安全,效率高
        //Vector 线程安全  效率低  ArrayList 线程不安全,效率高
        //难道是要性能不要安全吗,肯定不是。
        //在没有线程安全要求的情况下可以使用ArrayList
        //如果遇到了线程安全的情况怎么办
        //方法1:程序员手动的将不安全的变成安全的   
        //方法2:提供最新的线程安全并且性能高的集合类
        List list3 = new ArrayList();
        Collections.addAll(list3, 10, 90, 30, 40, 50, 23);
        System.out.println(list3);
        //将list3转换成线程安全的集合类
        list3 = Collections.synchronizedList(list3);
        //下面再操作,就线程安全了
    }
}

十六. 综合实战案例 - 简易电话本

​ 要求:

​ 1. 所有控制台输入、输出代码只能出现在SystemService的实现类中。

​ 2. 每个人必须输入两个电话号码,并且不可以重复。

​ 3. 查看电话本、录入联系人、删除联系人、修改联系人手机号码等所有功能都是联系人的相关功能。

​ 包含的功能:

​ 1. 查看电话本功能

​ a) 先查看所有的联系人

​ b) 选择具体的联系人, 再查看其电话号码

​ 2. 录入联系人功能

​ 3. 删除联系人功能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值