当前位置:   article > 正文

Java集合(Collection、List、Set、ArrayList、LinkedList及底层原理)_java arraylist linkedlist set

java arraylist linkedlist set

一、集合概述和分类

1.1 集合的分类

Java中的集合主要为两类,如下图所示:一类是单列集合元素是一个一个的,另一类是双列集合元素是一对一对的。

Collection是单列集合的根接口,Collection接口下面又有两个子接口List接口、Set接口,List和Set下面分别有不同的实现类,如下图所示:

上图中各种集合的特点如下图所示:

可以自己写代码验证一下,各种集合的特点

  1. //简单确认一下Collection集合的特点
  2. ArrayList<String> list = new ArrayList<>(); //存取顺序一致,可以重复,有索引
  3. list.add("java1");
  4. list.add("java2");
  5. list.add("java1");
  6. list.add("java2");
  7. System.out.println(list); //[java1, java2, java1, java2]
  8. HashSet<String> list = new HashSet<>(); //存取顺序不一致,不重复,无索引
  9. list.add("java1");
  10. list.add("java2");
  11. list.add("java1");
  12. list.add("java2");
  13. list.add("java3");
  14. System.out.println(list); //[java3, java2, java1]

1.2 Collection集合的常用方法

因为Collection为跟接口,所以Collection集合常用方法所有Collection实现类都可以使用。 这里我们以创建ArrayList为例,来演示

  1. Collection<String> c = new ArrayList<>();
  2. //1.public boolean add(E e): 添加元素到集合
  3. c.add("java1");
  4. c.add("java1");
  5. c.add("java2");
  6. c.add("java2");
  7. c.add("java3");
  8. System.out.println(c); //打印: [java1, java1, java2, java2, java3]
  9. //2.public int size(): 获取集合的大小
  10. System.out.println(c.size()); //5
  11. //3.public boolean contains(Object obj): 判断集合中是否包含某个元素
  12. System.out.println(c.contains("java1")); //true
  13. System.out.println(c.contains("Java1")); //false
  14. //4.pubilc boolean remove(E e): 删除某个元素,如果有多个重复元素只能删除第一个
  15. System.out.println(c.remove("java1")); //true
  16. System.out.println(c); //打印: [java1,java2, java2, java3]
  17. //5.public void clear(): 清空集合的元素
  18. c.clear();
  19. System.out.println(c); //打印:[]
  20. //6.public boolean isEmpty(): 判断集合是否为空 是空返回true 反之返回false
  21. System.out.println(c.isEmpty()); //true
  22. //7.public Object[] toArray(): 把集合转换为数组
  23. Object[] array = c.toArray();
  24. System.out.println(Arrays.toString(array)); //[java1,java2, java2, java3]
  25. //8.如果想把集合转换为指定类型的数组,可以使用下面的代码
  26. String[] array1 = c.toArray(new String[c.size()]);
  27. System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]
  28. //9.还可以把一个集合中的元素,添加到另一个集合中
  29. Collection<String> c1 = new ArrayList<>();
  30. c1.add("java1");
  31. c1.add("java2");
  32. Collection<String> c2 = new ArrayList<>();
  33. c2.add("java3");
  34. c2.add("java4");
  35. c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去
  36. System.out.println(c1); //[java1, java2, java3, java4]

Collection集合常用方法如下图所示,ArrayList、LinkedList、HashSet、LinkedHashSet、TreeSet集合都可以调用下面的方法。

二、Collection遍历方式

2.1 迭代器遍历集合

迭代器就是一种集合的通用遍历方式。

代码写法如下:

  1. Collection<String> c = new ArrayList<>();
  2. c.add("赵敏");
  3. c.add("小昭");
  4. c.add("素素");
  5. c.add("灭绝");
  6. System.out.println(c); //[赵敏, 小昭, 素素, 灭绝]
  7. //第一步:先获取迭代器对象
  8. //解释:Iterator就是迭代器对象,用于遍历集合的工具)
  9. Iterator<String> it = c.iterator();
  10. //第二步:用于判断当前位置是否有元素可以获取
  11. //解释:hasNext()方法返回true,说明有元素可以获取;反之没有
  12. while(it.hasNext()){
  13.    //第三步:获取当前位置的元素,然后自动指向下一个元素.
  14.    String e = it.next();
  15.    System.out.println(s);
  16. }

迭代器代码的原理如下:

  • 当调用iterator()方法获取迭代器时,当前指向第一个元素

  • hasNext()方法则判断这个位置是否有元素,如果有则返回true,进入循环

  • 调用next()方法获取元素,并将当月元素指向下一个位置,

  • 等下次循环时,则获取下一个元素,依此内推

使用迭代器遍历集合用到的方法

2.2 增强for遍历集合

迭代器遍历集合,其实还有一种更加简化的写法,叫做增强for循环。

格式如下:

需要注意的是,增强for不光可以遍历集合,还可以遍历数组。接下来我们用代码演示一emo下:

  1. Collection<String> c = new ArrayList<>();
  2. c.add("赵敏");
  3. c.add("小昭");
  4. c.add("素素");
  5. c.add("灭绝");
  6. //1.使用增强for遍历集合
  7. for(String s: c){
  8.    System.out.println(s);
  9. }
  10. //2.再尝试使用增强for遍历数组
  11. String[] arr = {"迪丽热巴", "古力娜扎", "稀奇哈哈"};
  12. for(String name: arr){
  13.    System.out.println(name);
  14. }

2.3 forEach遍历集合

在JDK8版本以后还提供了一个forEach方法也可以遍历集合,如果下图所示:

由上图可知forEach方法的参数是一个Consumer接口,而Consumer是一个函数式接口,所以可以传递Lambda表达式

  1. Collection<String> c = new ArrayList<>();
  2. c.add("赵敏");
  3. c.add("小昭");
  4. c.add("素素");
  5. c.add("灭绝");
  6. //调用forEach方法
  7. //由于参数是一个Consumer接口,所以可以传递匿名内部类
  8. c.forEach(new Consumer<String>{
  9.    @Override
  10.    public void accept(String s){
  11.        System.out.println(s);
  12.   }
  13. });
  14. //也可以使用lambda表达式对匿名内部类进行简化
  15. c.forEach(s->System.out.println(s)); //[赵敏, 小昭, 素素, 灭绝]

2.4 遍历集合案例

接下来,我们看一个案例,在集合中存储自定义的对象,并遍历。具体要求如下

首先,需要写一个电影类,用来描述每一步电影应该有哪些信息。

  1. public class Movie{
  2.    private String name; //电影名称
  3.    private double score; //评分
  4.    private String actor; //演员
  5.    //无参数构造方法
  6.    public Movie(){}
  7.    //全参数构造方法
  8.    public Movie(String name, double score, String actor){
  9.        this.name=name;
  10.        this.score=score;
  11.        this.actor=actor;
  12.   }
  13.    //...get、set、toString()方法自己补上..
  14. }

接着,再创建一个测试类,完成上面的需求

  1. public class Test{
  2.    public static void main(String[] args){
  3.        Collection<Movie> movies = new ArrayList<>();
  4.        movies.add(new MOvie("《肖申克的救赎》", 9.7, "罗宾斯"));
  5.        movies.add(new MOvie("《霸王别姬》", 9.6, "张国荣、张丰毅"));
  6.        movies.add(new MOvie("《阿甘正传》", 9.5, "汤姆汉克斯"));
  7.        
  8.        for(Movie movie : movies){
  9.            System.out.println("电影名:" + movie.getName());
  10.            System.out.println("评分:" + movie.getScore());
  11.            System.out.println("主演:" + movie.getActor());
  12.       }
  13.   }
  14. }

以上代码的内存原理如下图所示:当往集合中存对象时,实际上存储的是对象的地址值

三、List系列集合

List集合是Collection下面的一个子体系。如下图所示:

3.1 List集合的常用方法

List集合是索引的,所以多了一些有索引操作的方法,如下图所示:

接下来,用代码演示一下这几个方法的效果

  1. //1.创建一个ArrayList集合对象(有序、有索引、可以重复)
  2. List<String> list = new ArrayList<>();
  3. list.add("蜘蛛精");
  4. list.add("至尊宝");
  5. list.add("至尊宝");
  6. list.add("牛夫人");
  7. System.out.println(list); //[蜘蛛精, 至尊宝, 至尊宝, 牛夫人]
  8. //2.public void add(int index, E element): 在某个索引位置插入元素
  9. list.add(2, "紫霞仙子");
  10. System.out.println(list); //[蜘蛛精, 至尊宝, 紫霞仙子, 至尊宝, 牛夫人]
  11. //3.public E remove(int index): 根据索引删除元素, 返回被删除的元素
  12. System.out.println(list.remove(2)); //紫霞仙子
  13. System.out.println(list);//[蜘蛛精, 至尊宝, 至尊宝, 牛夫人]
  14. //4.public E get(int index): 返回集合中指定位置的元素
  15. System.out.println(list.get(3));
  16. //5.public E set(int index, E e): 修改索引位置处的元素,修改后,会返回原数据
  17. System.out.println(list.set(3,"牛魔王")); //牛夫人
  18. System.out.println(list); //[蜘蛛精, 至尊宝, 至尊宝, 牛魔王]

3.2 List集合的遍历方式

List集合相比于前面的Collection多了一种可以通过索引遍历的方式,所以List集合遍历方式一共有四种:

  • 普通for循环(只因为List有索引)

  • 迭代器

  • 增强for

  • Lambda表达式

  1. List<String> list = new ArrayList<>();
  2. list.add("蜘蛛精");
  3. list.add("至尊宝");
  4. list.add("糖宝宝");
  5. //1.普通for循环
  6. for(int i = 0; i< list.size(); i++){
  7.    //i = 0, 1, 2
  8.    String e = list.get(i);
  9.    System.out.println(e);
  10. }
  11. //2.增强for遍历
  12. for(String s : list){
  13.    System.out.println(s);
  14. }
  15. //3.迭代器遍历
  16. Iterator<String> it = list.iterator();
  17. while(it.hasNext()){
  18.    String s = it.next();
  19.    System.out.println(s);
  20. }
  21. //4.lambda表达式遍历
  22. list.forEach(s->System.out.println(s));

3.3 ArrayList底层的原理

ArrayList集合底层是基于数组结构实现的,也就是说当你往集合容器中存储元素时,底层本质上是往数组中存储元素。 特点如下:

可是数组的长度是固定的,但集合的长度却是可变的,这是怎么做到的呢?原理如下:

数组扩容,并不是在原数组上扩容(原数组是不可以扩容的),底层是创建一个新数组,然后把原数组中的元素全部复制到新数组中去。

3.4 LinkedList底层原理

LinkedList底层是链表结构,链表结构是由一个一个的节点组成,一个节点由数据值、下一个元素的地址组成。如下图所示

假如,现在要在B节点和D节点中间插入一个元素,只需要把B节点指向D节点的地址断掉,重新指向新的节点地址就可以了。如下图所示:

假如,现在想要把D节点删除,只需要让C节点指向E节点的地址,然后把D节点指向E节点的地址断掉。此时D节点就会变成垃圾,会把垃圾回收器清理掉。

上面的链表是单向链表,它的方向是从头节点指向尾节点的,只能从左往右查找元素,这样查询效率比较慢;还有一种链表叫做双向链表,不光可以从做往右找,还可以从右往左找。如下图所示:

LinkedList集合是基于双向链表实现了,所以相对于ArrayList新增了一些可以针对头尾进行操作的方法,如下图示所示:

3.5 LinkedList集合的应用场景

LInkedList集合有什么用呢?可以用它来设计栈结构、队列结构。

  • 先来认识一下队列结构,队列结构你可以认为是一个上端开口,下端也开口的管子的形状。元素从上端入队列,从下端出队列。

入队列可以调用LinkedList集合的addLast方法,出队列可以调用removeFirst()方法.

  1. //1.创建一个队列:先进先出、后进后出
  2. LinkedList<String> queue = new LinkedList<>();
  3. //入对列
  4. queue.addLast("第1号人");
  5. queue.addLast("第2号人");
  6. queue.addLast("第3号人");
  7. queue.addLast("第4号人");
  8. System.out.println(queue);
  9. //出队列
  10. System.out.println(queue.removeFirst()); //第4号人
  11. System.out.println(queue.removeFirst()); //第3号人
  12. System.out.println(queue.removeFirst()); //第2号人
  13. System.out.println(queue.removeFirst()); //第1号人
  • 接下来,再用LinkedList集合来模拟一下栈结构的效果。还是先来认识一下栈结构长什么样。栈结构可以看做是一个上端开头,下端闭口的水杯的形状。

    元素永远是上端进,也从上端出,先进入的元素会压在最底下,所以栈结构的特点是先进后出,后进先出

有没有感觉栈结构很像,手枪的子弹夹呀!!第一个压进入的子弹在最底下,最后一个才能打出来,最后一个压进入的子弹在最顶上,第一个打出来。

接着,就用LinkedList来模拟下栈结构,代码如下:

  1. //1.创建一个栈对象
  2. LinkedList<String> stack = new ArrayList<>();
  3. //压栈(push) 等价于 addFirst()
  4. stack.push("第1颗子弹");
  5. stack.push("第2颗子弹");
  6. stack.push("第3颗子弹");
  7. stack.push("第4颗子弹");
  8. System.out.println(stack); //[第4颗子弹, 第3颗子弹, 第2颗子弹,第1颗子弹]
  9. //弹栈(pop) 等价于 removeFirst()
  10. System.out.println(statck.pop()); //第4颗子弹
  11. System.out.println(statck.pop()); //第3颗子弹
  12. System.out.println(statck.pop()); //第2颗子弹
  13. System.out.println(statck.pop()); //第1颗子弹
  14. //弹栈完了,集合中就没有元素了
  15. System.out.println(list); //[]
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/971035
推荐阅读
相关标签
  

闽ICP备14008679号