集合2(List)

一.迭代器Iterator

是Collection,Set,Queue的父接口

Iterable 是一个接口,表示对象可以被迭代,也就是可以被遍历。

1.方法

修饰符方法名描述
default``voidforEach(Consumer<? super T> action)Iterable 的每个元素执行给定的操作,直到处理完所有元素或操作引发异常。
Iterator<T>iterator()返回类型为 T 的元素的迭代器。
default``Spliterator<T>spliterator()在这个 Iterable 描述的元素上创建一个 Spliterator

2.与for循环的区别

首先我们集合中arraylist添加元素

       ArrayList<String> list = new ArrayList();
        list.add(null);
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("c");
 for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if ("d".equals(s)){
                list.remove(i);
            }
            System.out.print(s);

这里需要注意的一点是,因为i循环条件是size(),yc,随着删除d元素,size也会变小,

例如e原来索引是i=5,删除后:i=4,因此输出时会略过删除元素的后一个元素

输出结果:null a b c c

Iterator
// for (String str : list){
//            if ("c".equals(str)){
 //               list.remove(str);
 //           }
 //           System.out.println(str);
 //     }

但是此处会报错:Exception in thread "main" java.util.ConcurrentModificationException

这里当删除的元素是索引倒数第二位的时候,会删除成功,因为删除成功以后迭代器会停止迭代,但是此时最后一位元素会因为删除的原因索引-1,因此很巧妙的完成了删除

  for (String str : list){
            if ("e".equals(str)){
                list.remove(str);
            }
            System.out.println(list);

        }

运行

image-20230905154546840

是因为代码中底层对删除和增加迭代器中设置

int expectedModCount = modCount;

当修改量expectedModCount与md不同时,会抛出异常

private void rangeCheckForAdd(int index) {
    if (index < 0 || index > size())
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

为此Iterator提供了方法:

remove

  1. 方法签名:

    javaCopy code
    void remove()
    
  2. 作用: remove() 方法从集合中删除迭代器的当前位置所指向的元素。它会修改集合的结构,通常会导致集合的大小减小。

  3. 支持情况: remove() 方法是一个可选操作,不是所有的迭代器都支持它。具体支持情况取决于集合类型和迭代器的实现。通常情况下,List 接口的迭代器(如 ArrayListLinkedList)支持 remove() 方法,而 Set 接口的迭代器(如 HashSetTreeSet)通常不支持。

  4. 使用限制: remove() 方法通常要求在调用之前必须先调用 next() 方法,以确保迭代器指向一个有效的元素。如果在调用 remove() 之前没有调用 next() 或者已经调用过 remove(),则会抛出 IllegalStateException 异常。

示例

        Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
            String str = iterator.next();
            if ("c".equals(str)){
//                list.remove(str);
                iterator.remove();
                continue;
            }
            System.out.println(str);
        }

此处可以发现,arraylist中相比其他集合不同的特性

特性

  1. 动态数组: ArrayList 是基于动态数组实现的,它可以自动增长和缩小以适应元素的添加和删除。这意味着它的大小可以根据需要动态调整。
  2. 有序集合: ArrayList 是有序集合,它按照元素添加的顺序来维护元素。你可以通过索引访问元素,索引从0开始,以数组的方式进行访问。
  3. 允许重复元素: ArrayList 允许存储重复的元素,同一个元素可以出现多次。
  4. 随机访问: 由于 ArrayList 使用数组来存储元素,所以可以通过索引进行随机访问,访问速度非常快。
  5. 不同步: ArrayList 不是线程安全的,这意味着在多线程环境下使用时需要考虑同步问题。如果需要线程安全的集合,可以考虑使用 VectorCollections.synchronizedList() 方法包装一个 ArrayList
  6. 支持泛型: 从 Java 5 开始,ArrayList 支持泛型,允许你指定集合中存储的元素类型,以提高类型安全性。
  7. 可变大小: 你可以随时向 ArrayList 中添加元素或删除元素,而不需要事先指定容量。
  8. 效率: ArrayList 在访问元素时非常高效,但在插入或删除元素时,如果需要移动后续元素,可能会较慢。因此,对于频繁插入和删除操作,考虑使用 LinkedList

注:arraylist允许存放null值

二.Arraylist

LinkedList 的直接父类是 AbstractSequentialList ,实现了 List 、 Deque

image-20230905173429770

vector和arraylist区别

  1. 线程安全性
    • Vector 是线程安全的,所有方法都是同步的,这意味着多个线程可以同时访问和修改 Vector 实例,而不会导致数据不一致或冲突。
    • ArrayList 不是线程安全的,它的方法没有同步,如果多个线程同时访问或修改一个 ArrayList 实例,可能会导致数据不一致或抛出并发异常。
  2. 性能
    • 由于 Vector 的方法都是同步的,因此它的性能通常比 ArrayList 差。在多线程环境中,Vector 可能会更安全,但也更慢。
    • ArrayList 没有同步开销,因此在单线程环境中通常比 Vector 快。
  3. 增长策略
    • VectorArrayList 都支持自动增长,但它们的增长策略不同。
    • Vector 的增长策略是每次增加当前容量的一半,而 ArrayList 增长策略是每次增加当前容量的一倍。这意味着 ArrayList 可能会比 Vector 更有效地处理动态大小的数组。
  4. 初始容量
    • Vector 的默认初始容量是 10,但可以通过构造函数指定初始容量。
    • ArrayList 的默认初始容量也是 10,但可以根据需要进行扩展。

三.LinkedList

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable

1.方法

方法名返回值描述
addFirst(E e)void在该列表开头插入指定的元素
addLast(E e)void将指定的元素追加到此列表的末尾(等同于 add()
get(int index)E返回此列表中指定位置的元素
getFirst()E返回此列表中的第一个元素
getLast()E返回此列表中的最后一个元素
push(E e)void列表的前面插入元素(等同于 addFirst()
removeFirst()E从此列表中删除并返回第一个元素(等同于 poll()pollFirst()pop()

ArrayList / LinkedList的区别:

  1. 底层实现:

    • ArrayList:基于数组实现**,默认容量是 10** ...。内部使用数组来存储元素,支持随机访问,通过索引访问元素的时间复杂度为 O(1)。

    • 内存, ArrayList 使用的是连续空间

    • LinkedList:基于双向链表实现。内部使用链表来存储元素,不支持随机访问,访问元素的时间复杂度取决于元素的位置,平均为 O(n/2)。

  2. 插入和删除操作:

    • ArrayList:在尾部添加元素时性能良好,但在中间或头部插入/删除元素时性能较低,因为需要移动后面的元素。
    • LinkedList:在头部和尾部插入/删除元素时性能良好,而在中间插入/删除元素时性能良好,因为只需修改相邻元素的引用。
  3. 随机访问:

    • ArrayList:支持随机访问,可以通过索引直接访问元素,因此在读取元素时性能较好。
    • LinkedList:不支持随机访问,必须从头部或尾部开始遍历链表来访问元素,因此在读取元素时性能较差。
  4. 内存占用:

    • ArrayList:由于需要分配一块连续的内存空间来存储元素,可能会浪费一些内存空间。
    • LinkedList:每个元素都包含对前后元素的引用,因此可能会占用更多的内存。
  5. 迭代性能:

    • ArrayList:迭代时性能良好,因为可以通过索引直接访问元素。
    • LinkedList:迭代时性能较差,因为必须从头部或尾部开始遍历链表。
  6. 应用场景:

    • ArrayList:适用于需要频繁读取元素的场景,如遍历、搜索。
    • LinkedList:适用于需要频繁插入和删除元素的场景,如队列、栈。

四.Set

不包含重复元素的集合、不能保证存储的顺序、只允许有一个 null

public interface Set<E>
extends Collection<E>

Set 集合的实现类有很多,在此我们重点了解 HashSet 、 TreeSet 、 LinkedHashSet

1.TreeSet

public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable

1.特点:

  • 非线程安全
  • 值必须可比较(元素实现 Comparable 接口、传递 比较器 Comparator 对象)
  • 不能存 null
  • 判断是否是重复元素,是按照自然比较/比较器进行比较

就是说a.compareTo(b) == 0,如果是 true ,那么 add(a) 之后的 add(b) 将会返回 false ,
也就是添加失败

2.常用的构造方法

方法名描述
TreeSet()构造一个新的空 TreeSet 集合,根据其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)构造一个新的空 TreeSet 集合,根据指定的比较器进行排序。
TreeSet(Collection<? extends E> c)构造一个新的 TreeSet 集合,该 TreeSet 集合包含指定集合中的元素,并根据其元素的自然顺序进行排序。

3.常用方法

( Collection 接口的方法不在此赘述)

方法名返回值描述
ceiling(E e)E返回此集合中大于或等于给定元素的最小元素,如果没有这样的元素,则返回 null
first()E返回当前在此集合中的第一个(最低的)元素。
floor(E e)E返回此集合中小于或等于给定元素的最大元素,如果没有这样的元素,则返回 null
headSet(E toElement)SortedSet<E>返回此集合中元素严格小于 toElement 的部分的视图。
higher(e)E返回此集合中严格大于给定元素的最小元素,如果没有这样的元素,则返回 null
last()E返回当前在此集合中的最后一个(最高的)元素。
lower(e)E返回此集合中严格小于给定元素的最大元素,如果没有这样的元素,则返回 null
pollFirst()E检索并删除第一个(最低的)元素,如果此集合为空,则返回 null
pollLast()E检索并删除最后一个(最高)元素,如果此集合为空,则返回 null
tailSet(E fromElement)SortedSet<E>返回此集合中元素严格大于或等于 fromElement 的部分的视图。

示例:

TreeSet<String> set = new TreeSet(List.of("null", "a", "a", "b", "c", "e", "f",
"g"));
System.out.println(set); // [a, b, c, e, f, g, null]
// 返回此集合中大于或等于给定元素的最小元素,如果没有这样的元素,则返回null。
String ceiling = set.ceiling("d");
System.out.println(ceiling);// e
// 返回当前在此集合中的第一个(最低的)元素。
String first = set.first();
System.out.println(first); // a
// 返回此集合中小于或等于给定元素的最大元素,如果没有这样的元素,则返回null
String floor = set.floor("d");
System.out.println(floor); // c
// 返回此集合中元素严格小于toElement的部分的视图。
SortedSet<String> headSet = set.headSet("c");
System.out.println(headSet); // a, b
// 返回此集合中严格大于给定元素的最小元素,如果没有这样的元素,则返回null。
String higher = set.higher("c");
System.out.println(higher); // e
// 返回此集合中元素严格大于或等于fromElement的部分的视图。
SortedSet<String> tailSet = set.tailSet("c");
System.out.println(tailSet); // c, e, f, g, null
// 迭代
for (Object obj : set){
System.out.println(obj);
}

HashSet

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable

特点

  • 实现了 Set 接口,底层实现是 HashMap 。不保证迭代顺序,允许 null 元素
  • 非线程安全的
  • 如果 add 的值已存在( equals 方法返回 true ,基本数据类型自动装箱)返回 false
  • 如果 HashSet 中存的是对象,需要重写此对象类中的 equals 和 hashCode() 方法

1.构造方法

方法名描述
HashSet()构造一个新的空集合,默认初始容量为 16,负载因子为 0.75。底层实现是 HashMap
HashSet(Collection<? extends E> c)构造一个新的集合,包含指定集合中的元素。
HashSet(int initialCapacity)构造一个新的空集合,指定初始容量。
HashSet(int initialCapacity, float loadFactor)构造一个新的空集合,指定初始容量和负载因子。(负载因子指到达容量多少进行扩容)

示例:

   import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        // 构造一个新的空集合,默认初始容量为 16,负载因子为 0.75
        HashSet<String> set1 = new HashSet<>();
        set1.add("apple");
        set1.add("banana");
        set1.add("cherry");
    // 构造一个新的集合,包含指定集合中的元素
    HashSet<String> set2 = new HashSet<>(set1);
    set2.add("date");

    // 构造一个新的空集合,指定初始容量
    HashSet<Integer> set3 = new HashSet<>(20);
    set3.add(1);
    set3.add(2);
    set3.add(3);

    // 构造一个新的空集合,指定初始容量和负载因子
    HashSet<Double> set4 = new HashSet<>(10, 0.5f);
    set4.add(1.1);
    set4.add(2.2);

    // 输出各个集合的内容
    System.out.println("set1: " + set1);
    System.out.println("set2: " + set2);
    System.out.println("set3: " + set3);
    System.out.println("set4: " + set4);

    // 使用方法示例
    String floor = set1.floor("d");
    System.out.println("floor(\"d\"): " + floor); // 输出:cherry

    HashSet<String> headSet = new HashSet<>(set1.headSet("c"));
    System.out.println("headSet(\"c\"): " + headSet); // 输出:[apple, banana]

    String higher = set1.higher("c");
    System.out.println("higher(\"c\"): " + higher); // 输出:null

    HashSet<String> tailSet = new HashSet<>(set1.tailSet("c"));
    System.out.println("tailSet(\"c\"): " + tailSet); // 输出:[cherry]

    // 迭代示例
    System.out.println("迭代 set1:");
    for (String element : set1) {
        System.out.println(element);
    }
}
}

2.常用方法:

方法名描述
add(E e)将指定元素添加到集合中。如果元素已经存在,添加操作将被忽略,不会抛出异常。返回 true 表示添加成功,返回 false 表示添加失败。
remove(Object o)从集合中移除指定元素。如果元素存在且被成功移除,返回 true,否则返回 false
contains(Object o)检查集合是否包含指定元素。如果集合包含该元素,返回 true,否则返回 false
isEmpty()检查集合是否为空。如果集合中没有元素,返回 true,否则返回 false
size()返回集合中的元素数量。
clear()清空集合,移除所有元素。
iterator()返回一个迭代器,用于遍历集合中的元素。
addAll(Collection<? extends E> c)将另一个集合中的所有元素添加到当前集合中。