Java基础差缺补漏系列之集合系列2

简介: Java基础差缺补漏系列之集合系列2

linkedlist

底层为链表

LinkedList 常用方法:


增加:addFirst(E e),addLast(E e),offer(E e),offerFirst(E e),offerLast(E e)

删除:poll(),pollFirst(),pollLast(),removeFirst(),removeLast()

修改:set(int index, E element)

查看:element(),getFirst(),getLast(),indexOf(Object o),lastIndexOf(Object o),peek(),peekFirst(),peekLast()


判断:

示例代码:

//    现有一个linkedlist集合对象
        public static void main(String[] args) {
            LinkedList<String> list = new LinkedList();
//          能否重复添加数据
            list.add("aaaa");
            list.add("bbbb");
            list.add("aaaa");
            list.add("cccc");
            System.out.println(list);
//            收尾添加
            list.addFirst("ffff");
            list.addLast("zzzz");
            System.out.println(list);
//            添加元素到尾端
            list.offer("ll");
            System.out.println(list);
            list.offerFirst("pp");
            list.offerLast("rr");
//            删除首个元素
            System.out.println(list);
            System.out.println("删除"+list.poll());//删除头上的元素,并且返回删除的元素
            System.out.println("删除"+list.pollFirst());
            System.out.println("删除"+list.pollLast());
            System.out.println(list);
//查找头尾元素
            System.out.println(list.peek());
            System.out.println(list.peekLast());
//            list.clear();
//           方法的举例
            System.out.println(list);
            System.out.println(list.pollLast());//如果没有返回null,不影响程序执行
            System.out.println(list.removeFirst());//Exception in thread "main" java.util.NoSuchElementException,会报错,
//            遍历
            System.out.println("----------");
//            1
            for (int i=0; i<list.size();i++){
                System.out.println(list.get(i));
            }
//            2
            for (String s : list) {
                System.out.println(s);
            }
//            3
            Iterator it = list.iterator();
            while (it.hasNext()){
                System.out.println(it.next());
            }
//            可能源码看到,这个迭代器相对好,内存上的节省
            for ( Iterator it1 = list.iterator();it1.hasNext();){
                System.out.println(it1.next());
            }

为什么有功能相同的方法:以


pollFirst(),pollLast(),


removeFirst(),removeLast()


为例:


removeFirst是早版本的


pollFirst是jdk1.6版本的


实测区别:

//           方法的举例
            System.out.println(list);
            System.out.println(list.pollLast());//如果没有返回null,不影响程序执行
            System.out.println(list.removeFirst());//Exception in thread "main" java.util.NoSuchElementException,会报错,

同样是空的集合,pollFirst删除第一个如果没有返回null,无报错,removeFirst会报错没有数据


相比之下1.6之后的方法提高了健壮性,其他类似方法与这一对一致,


遍历方式:

源码看到,这个迭代器相对好,内存上的节省,例子:


for ( Iterator it1 = list.iterator();it1.hasNext();){
                System.out.println(it1.next());
            }


linkedlist的原理

对比学习:

1.png


Linkedlist是双向链表:

简要底层原理图:


1.png


模拟一个linkedList

首先是我们的节点类

package linkedListPrc;
import javax.xml.soap.Node;
public class Test02 {//节点类
//    三个属性
//    上一个元素的地址
private Test02 pre;
//    当前存放的元素
private Object obj;
//    下一个元素的地址
private Test02 next;
    public Test02 getPre() {
        return pre;
    }
    public void setPre(Test02 pre) {
        this.pre = pre;
    }
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
    public Test02 getNext() {
        return next;
    }
    public void setNext(Test02 next) {
        this.next = next;
    }
    public Test02() {
    }
}

之后就是我们的mylist源码


public class MyLinkedList {
//   链中一个定有一个首节点
Test02 first;
//    链中一个有一个尾节点
Test02 last;
//    计数器
    int count =0;
//    提供一个构造器
    public  MyLinkedList(){
    }
//    元素添加方法
    public void add(Object o){
        if (first ==null){//证明添加的元素是第一个
//            那接下来我们需要吧添加的元素封装成一个node
            Test02 n = new Test02();
            n.setPre(null);
            n.setObj(o);
            n.setNext(null);
            first = n;
            last = n;
        }else {//证明以及不是链中的第一个节点了
//            先把添加的元素封装成一个node对象
            Test02 n = new Test02();
            n.setPre(last);
            n.setObj(o);
//            当前链中的最后一个节点的下一个元素要指向n
            last.setNext(n);
//            将最后一个节点变成n
            last = n;
        }
        count++;
    }
    public int getSize(){
        return count;
    }
//    通过下标得到元素
    public  Object getelByindex(int index){
        if (index>count){
            System.out.println("这个索引上没有数据,检查插入的元素是否达到查找索引的数量");
        }
//        获取链表的头元素
        Test02 n = first;
        for (int i = 0;i< index;i++){
//          一路next找到想要的元素
                n = n.getNext();
        }
          return n.getObj();
    }
}


linkdelist部分源码解读

public class LinkedList<E>{
     //长度
   transient int size = 0;
     //首位
    transient Node<E> first;
     //尾位
    transient Node<E> last;
     //封装节点对象方法
        private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
     //首元素
        transient Node<E> first;
     //尾元素
        transient Node<E> last;
   //空构造器
            public LinkedList()   {
    }
    //添加元素操作
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
      void linkLast(E e) { //添加的元素e
        final Node<E> l = last;
           //将链表中的last节点给l,如果是第一个元素的话l为null
        final Node<E> newNode = new Node<>(l, e, null);
          //将元素封装为一个node的具体对象
        last = newNode;
           //将链表的为节点指向新创建的对象
        if (l == null)如果添加的是第一个节点
            first = newNode;//将链表的first节点只想为新节点
        else //如果添加的不是第一个节点
            l.next = newNode;//将l的下一个指向新的节点
        size++; //集合中元素数量加1
        modCount++;
    }
    //查找方法
        public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    //二分查找的方式遍历链表
        Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
}


迭代器

迭代器的源码,以及两个经典方法


1.png


面试题:迭代器对应的关系


hashnext与next方法的具体实现

1.png


增强for循环起始底层也是用迭代器完成的

用debug查看增强for循环,会发现,执行的下一步还是会进到iterator里


listirerator

加入字符串,比如我们想想要在代码中遍历到cc后新增一个kk

       List list = new ArrayList();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            if ("cc".equals(iterator.next())){
                list.add("kk");
            }
        }


控制台发生并发修改异常

1.png

出错原因:


迭代器和lsit同时操作集合,

1.png




如何解决:事情让一个迭代器做,新迭代器:listiterator,迭代和添加操作都用listiterator来操作

        ListIterator iterator = list.listIterator();
        while (iterator.hasNext()){
            if ("cc".equals(iterator.next())){
                iterator.add("kk");
            }
        }



相关文章
|
29天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
38 3
|
9天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
18 2
|
9天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
13天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
13天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
13天前
|
Java 开发者
|
26天前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
52 5
|
27天前
|
安全 Java 程序员
Java集合之战:ArrayList vs LinkedList,谁才是你的最佳选择?
本文介绍了 Java 中常用的两个集合类 ArrayList 和 LinkedList,分析了它们的底层实现、特点及适用场景。ArrayList 基于数组,适合频繁查询;LinkedList 基于链表,适合频繁增删。文章还讨论了如何实现线程安全,推荐使用 CopyOnWriteArrayList 来提升性能。希望帮助读者选择合适的数据结构,写出更高效的代码。
52 3
|
13天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
12 0
|
18天前
|
Java API Apache
java集合的组内平均值怎么计算
通过本文的介绍,我们了解了在Java中计算集合的组内平均值的几种方法。每种方法都有其优缺点,具体选择哪种方法应根据实际需求和场景决定。无论是使用传统的循环方法,还是利用Java 8的Stream API,亦或是使用第三方库(如Apache Commons Collections和Guava),都可以有效地计算集合的组内平均值。希望本文对您理解和实现Java中的集合平均值计算有所帮助。
23 0