java集合笔记---未完待续

# 1、Collection接口和常用方法

## 1、Colleations接口的方法

```java
        //add添加单个元素G
        List list = new ArrayList();
        list.add("1");
        list.add(10);//list.add(new Integer(10))
        list.add(true);
        System.out.println("list="+list);
        //remove 删除指定元素
//        list.remove(1);//删除第2个元素
        list.remove("1");//删除指定对象
        System.out.println("list="+list);
        //contains :查找元素是否存在
        System.out.println(list.contains(10));//true
        //size :获取元素个数
        System.out.println(list.size());//2
        //isEmpty:判断是否为空
        list.clear();
        //System.out.println("list="+list);
        //addAll:添加多个元素
        ArrayList<Object> list1 = new ArrayList<>();
        list1.add("java");
        list1.add("python");
        list.addAll(list1);
        System.out.println("list="+list);
        //containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list1));//true

        //removeAll:删除多个元素
        list.add("c++");
        list.removeAll(list1);
        System.out.println("list="+list);
```

## 2、Collection接口遍历元素的方式

### 1、使用Iterator迭代器

​    1、Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。

​    2、所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了iterator接口的对象,即可以返回一个迭代器。

​    3、iterator的结构;

![image-20230204102423910](D:\study\Java_study\image\iterator.png)

​    4、iterator近用于遍历集合,iterator本身并不存放对象

​    **注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测,否则会抛出异常**

```java
package com.demo.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionIterator {
    public static void main(String[] args) {
        Collection arrayList = new ArrayList();
        arrayList.add(new Book("java","程序员1",1));
        arrayList.add(new Book("python","程序员2",2));
        arrayList.add(new Book("c++","程序员3",3));

        System.out.println("arrayList"+arrayList);
        //1.先得到arraylist对应的迭代器
        Iterator iterator=arrayList.iterator();
        System.out.println(iterator);
        while (iterator.hasNext()){//判断是否还有数据
            //返回下一个元素,类型是Object
            Object obj=iterator.next();
            System.out.println("obj"+obj);
        }
        //3.当迭代器退出while循环后,这时iterator指向了最后的元素,所以需要重制迭代器
        //也可以说获取一个新的迭代器
        iterator=arrayList.iterator();
        System.out.println(iterator);
        //while循环快捷键 快速生成 while ===>itit
        //显示所有快捷键 ctrl+j
    }
}
class Book{
    private String bookName;

    public Book(String bookName, String name, int age) {
        this.bookName = bookName;
        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;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    private String name;
    private int age;

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

```

### 2、for循环增强

增强for循环不仅可以在集合中使用,也可以在数组中使用。

增强for的底层也是迭代器;

增强for可以理解成简化版的 迭代器遍历;

# 2、list接口和常用方法

## 1、list接口的基本介绍

list接口时Collection接口的子接口;

```java
//list集合类中元素有序(即添加顺序和取出顺序一致,且可重复
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println("list"+list);
//2.list集合中的每个元素都有其对应的顺序索引,即支持索引
//  索引是从0开始的
System.out.println(list.get(0));
```

常用的有ArrayList、LinkedList和Vector

## 2、List接口的常用方法

```java
        List list = new ArrayList();
        list.add("张三丰");
        list.add("贾宝玉");
        //void add(int index,Object ele):在index位置插入ele元素
        //在index = 1 的位置上插入一个对象

        list.add(1,"刘备");
        //boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
        List list1=new ArrayList<>();
        list1.add("java");
        list.addAll(1,list1);
        list.add("张三丰");
        System.out.println("list"+list);
        //Object get(int index):获取指定index位置的元素
        //int indexOf(Object obj):返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("java"));
        // int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
        System.out.println(list.lastIndexOf("张三丰"));
        //Object remove(int index):移除指定index位置的元素,并返回此元素
        System.out.println(list.remove(4));
        //Object set(int index,Object ele):设置指定index位置的元素ele,相当于是替换。
        list.set(2,"python");//如果设置索引不存在则会抛出异常
        System.out.println("list"+list);
        //List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
        //  注意返回的子集合 fromIndex <= subList  <
        //返回区间是前闭后开的
        List list2=list.subList(0,2);
        System.out.println("List2"+list2);
```

### 3、list的三种遍历方式

```java
        System.out.println("===========迭代器=========");
        Iterator iterator=list.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
        System.out.println("========普通的for循环==========");
        for (int j = 0; j < list.size(); j++) {
            System.out.println(list.get(j));
        }
        System.out.println("========增强for循环==========");
        for (Object obj :list) {
            System.out.println(obj);
        }
```

# 3、ArrayLisr底层结构和源码分析

arryalist可以加入null,并且可以放多个

```java
    //ArrayList是线程不安全的,查看源码,没有 synchronized 关键字修饰
    /*
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
     */
    List list = new ArrayList();
    list.add(null);
    list.add("java");
    list.add(null);
    System.out.println(list);
```

## 1、ArrayList的底层操作机制源码分析(重难点)

### 1、ArrayList中维护了一个Object类型的数组elementData.

```java
transient Object[] elementData;        //transient 表示瞬间,短暂的,表示该属性不会被序列化
```

### 2、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData的1.5倍。

创建了一个空的elementData空数组

```java
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
```

执行list.add

​    1.先确定是否要扩容

​    2.然后再执行赋值操作

```java
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
```

```java
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
```

1.modcount++记录当前集合被修改的次数

2.如果若elementData的大小不够就调用group去扩容

```java
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
```

1.扩容方法

2.使用扩容机制来确定要扩容到多大

3.第一次newCapacity =10

4.第二次及其以后按照1.5倍扩容

5.扩容使用的是Arrays.copyOf()

```java
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
```

### 3、如果使用的是指定大小的构造器,则初始elementData 容量为指定大小,如需要再次扩容,则直接扩容elementData的1.5倍。

分析有参构造器创建和使用ArrayList的源码

创建了一个指定大小的elementData数组

this.elementData = new Object[initialCapacity];

```java
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
```

有参构造器的扩容机制第一次扩容就按照elementData的1.5倍扩容

其他原理与不指定大小的构造器相同

## 2、Vector底层结构和源码剖析

### 1、Vector累的定义说明

```java
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
```

### 2、Vector底层也是一个对象数组,protected Object[] elementData;

### 3、Vector 是线程同步的,即线程安全,Vector累的操作方法带有synchronized

```java
public synchronized int capacity() {
    return elementData.length;
}
```

### 4、在开发中,需要线程同步安全时,考虑用Vector

### 5、源码分析

```java
package com.demo.list;

import java.util.Vector;

public class Vector_ {
    public static void main(String[] args) {
        //无惨构造器
        //1、构造器
         //1.1new Vector()无参构造底层
         /*
            public Vector() {
                    this(10);
                }
          */
         //1.2Vector 有参构造器       
        /*
            public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
         */
        Vector vector = new Vector();
        for (int i = 0; i < 10; i++) {
            vector.add(i);
        }
        vector.add(100);
        //2.Vector.add(i)
        //2.1下面这个方法就是添加数据到Vector集合
    /*
        public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
     */
        //2.2 确定是否需要扩容 条件 :minCapacity - elementData.length >0
    /*
        private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
     */
        //2.3 如果需要的数组大小 不够用 ,就扩容,扩容算法int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
        //                                         capacityIncrement : oldCapacity);
    /*
        private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
     */
    }
}
```

## 3、Vector底层结构和ArrayList的比较

![Vector底层和ArrayList的比较](D:\study\Java_study\image\Vector底层和ArrayList的比较.png)

## 4、LinkedList底层结构

### 1、LinkedList 底层实现了双向链表和双端队列特点

### 2、可以添加任意元素(元素可重复),包括null

### 3、线程不安全,没有实现同步

### 4、LinkedList的底层操作机制

​    1.LinkedList底层中维护了一个双向链表

​    2.LinkedList中维护了两个属性first和last分别指向 首节点和尾结点

​    3.每个节点(Node)对象,里面又维护了Prev、next、item、 三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表。

​    4.所以LinkedList 的元素的添加和删除,不是通过数组完成的,相对来说效率较高。

​    5.模拟一个简单的双向链表 LinkedList01.java

```java
package com.demo.list;

public class LinkedList01 {
    public static void main(String[] args) {
        //模拟简单的双向链表
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node java = new Node("java");

        //连接三个节点,形成双向链表
        //jack -> tom -> java
        jack.next=tom;
        tom.next=java;
        //java -> tom -> jack
        java.pre=tom;
        tom.pre=jack;

        Node first = jack;  //让first引用指向jack ,就是双向列表的头结点
        Node last = java;   //让last引用指向java,就是双向列表的尾结点


        //演示:遍历
        System.out.println("first遍历");
        while (true){
            if (first==null){
                break;
            }else {
                System.out.println(first);
                first=first.next;
            }
        }
        System.out.println("last遍历");
        while (true){
            if (last==null){
                break;
            }else {
                System.out.println(last);
                last=last.pre;
            }
        }

        //演示:链表添加对象/数据
        //要求:在Tom和java之间添加一个对象 Python
        //1.先创建一个Node节点 ,name就是Python
        Node python = new Node("Python");
        python.next=java;
        python.pre=tom;
        tom.next=python;
        java.pre=python;
        //让first 再次指向jack
        first=jack;
        //让last重新指向java
        last=java;
        //演示:遍历
        System.out.println("first遍历");
        while (true){
            if (first==null){
                break;
            }else {
                System.out.println(first);
                first=first.next;
            }
        }
        System.out.println("last遍历");
        while (true){
            if (last==null){
                break;
            }else {
                System.out.println(last);
                last=last.pre;
            }
        }
    }
}

//定义一个node类,node对象 表示双向链表的一个节点
class Node{
    public Object item;//真正存放数据的地方
    public Node next;
    public Node pre;
    public Node(Object name){
        this.item=name;
    }

    @Override
    public String toString() {
        return "Node item=" + item ;
    }
}
```

### 5、LinkedList底层结构

```java
package com.demo.list;

import java.util.LinkedList;

public class LinkedListCRUD {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add("1");
        linkedList.add("2");

        System.out.println(linkedList.remove());//这里默认删除的是第一个元素
        System.out.println("linkedList="+linkedList);
    }
    /* 1.构造器
            public LinkedList() {
            }
        2.这时 linkedList 的属性 first = null last= null
        3.执行添加
           public boolean add(E e) {
                linkLast(e);
                return true;
            }
        4.将新的节点,加入到双向链表的最后
          void linkLast(E e) {
                final Node<E> l = last;
                final Node<E> newNode = new Node<>(l, e, null);
                last = newNode;
                if (l == null)
                    first = newNode;
                else
                    l.next = newNode;
                size++;
                modCount++;
            }
        5.删除过程分析
            5.1.执行linkedList.remove();//这里默认删除的是第一个元素
            5.2 执行remove()
                public E remove() {
                      return removeFirst();
                }
            5.3 执行 removeFirst()
                public E removeFirst() {
                    final Node<E> f = first;
                    if (f == null)
                        throw new NoSuchElementException();
                    return unlinkFirst(f);
                }
            5.4 执行unlinkFirst() 将f指向的双向链表的第一个节点拿掉
            private E unlinkFirst(Node<E> f) {
                // assert f == first && f != null;
                final E element = f.item;
                final Node<E> next = f.next;
                f.item = null;
                f.next = null; // help GC
                first = next;
                if (next == null)
                    last = null;
                else
                    next.prev = null;
                size--;
                modCount++;
                return element;
            }
     */

}
```

### 6、ArrayList和LinkedList比较

![ArrayList和LinkedList的比较](D:\study\Java_study\image\ArrayList和LinkedList的比较.png)

# 5、Set接口

## 1、Set接口基本介绍

·无序(添加和取出的顺序不一致),没有索引

·不允许重复元素,最多包含一个null

·和List接口一样,他都是继承自Collection接口,因此常用方法与list一样

·set接口的遍历方式只有迭代器和增强for循环

```java
public static void main(String[] args) {
    //1.以Set接口的实现类 HashSet来讲解Set接口的方法
    //2. set 接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
    //3. Set 接口存放数据是无序的(添加顺序和取出顺序不一致)
    //4. 注意:取出的顺序虽然不是添加的顺序,但是它是固定的
    HashSet set = new HashSet();
    set.add("1");
    set.add("2");
    set.add(2);


    set.add(null);
    set.add(null);


    System.out.println(set);
    set.add("测试");
    System.out.println(set);

    //遍历
    System.out.println("=====使用迭代器=====");
    Iterator iterator = set.iterator();
    while (iterator.hasNext()) {
        Object obj =  iterator.next();
        System.out.println("obj="+obj);
    }
    System.out.println("======增强for循环=====");
    for (Object obj:set) {
        System.out.println("obj="+obj);
    }
    //set接口对象不能通过索引获取对象
}
```

## 2、HashSet

HashSet的底层就是HashMap

```java

        //1. 构造器源码
        /*
            public HashSet() {
        map = new HashMap<>();
    }
         */
        //2.可以存放null,但只有一个null
        //3.HashSet不保证元素是有序的,取决于hash后,在确定索引的结果。(即,不保证存放元素与取出元素的顺序一致)
```

HashMap底层是(数组+链表+红黑树)

添加一个元素是,先得到hash值-会转成-》索引值

找到存储数据表table,看这个索引位置是否已经存放的有元素

如果没有,直接加入

如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后

**在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREELFY_CAPACITY(64)。就会树化(红黑树)**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~贝母~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值