集合的知识点

news2024/12/28 18:21:55

一、集合的简介

1.1 什么是集合

集合(Collection),也是一个数据容器,类似于数组,但是和数组是不一样的。集合是一个可变的容器,可以随时向集合集合中添加元素,也可以随时从集合中删除元素。另外,集合还提供了若干个用来操作集合中数据的方法。

集合里的数据,我们称之为元素(elements);集合只能用来存储集合只能用来存储引用类型的数据不能存储八大基本数据类型的数据

1.2 泛型的引入

Java SE 5.0以前,集合的元素只要是Object类型就行,那个时候任何对象都可以存放在集合内,但是从集合中获取对象后,需要进行正确的强制类型转换。但是,Java SE 5.0 以后,可以使用新特性”泛型”,用来指定要存放在集合中的对象类型。避免了强制转换的麻烦。

1.3 集合与数组

1. 数组是定长的容器,一旦实例化完成,长度不能改变。集合是可变长的,可以随时的进行增删操作。

2. 数组中可以存储基本数据类型和引用数据类型的元素,集合中只能存储引用数据类型的元素。

3. 数组的操作比较单一,只能通过下标进行访问。集合中提供了若干个方便对元素进行操作的方法。

小贴士:存储引用类型时,集合与数组,存储的其实都是对象的地址

1.4 集合框架体系图

二、Collection接口 

2.1 简介

Collection接口是子接口List/Set/Queue的父接口,里面提供了子类共同的常用方法。

既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

作为父接口,其子类集合的对象,存储元素的特点,可能是无序的,也可能是有序的,因此在父接口中并没有定义通过下标获取元素的方法功能。

2.2 常用方法

1)boolean add(E e) : 向集合中添加元素。

//使用多态的向上造型创建一个子类型对象
Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
System.out.println(c1);
System.out.println(c1.add("D"));//true
运行结果:
[A, B, C]
true

2)boolean isEmpty():判断一个集合是不是空的。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
boolean empty = c1.isEmpty();
System.out.println("empty: "+empty);
运行结果:false

3)int size():获取集合里有几个元素。 

int index = c1.size();
System.out.println("index: "+index);

4) boolean addAll(Collection c) : 向集合中添加另一个集合的全部。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
Collection<String> c2 = new ArrayList<>();
c2.add("B");
c2.add("C");
c2.add("D");
c1.addAll(c2);
System.out.println(c1);
运行结果:[A,B,C,B,C,D]

5)boolean contains(Object o):查看是否包含子字符串o。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
boolean c = c1.contains("B");
System.out.println("是否包含字符串B: "+c);
运行结果:是否包含字符串B: true

6)boolean containsAll(Collection c):查看集合是否包含 一个子集合。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
Collection<String> c2 = new ArrayList<>();
c2.add("B");
c2.add("C");
c2.add("D");
boolean d = c1.containsAll(c2);
System.out.println("c1集合是否包含c2集合: "+d);
运行结果:c1集合是否包含c2集合: false

7)boolean equals(Object o):判断两个集合是否相等。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
Collection<String> c2 = new ArrayList<>();
c2.add("B");
c2.add("C");
c2.add("D");
boolean e  = c1.equals(c2);
System.out.println("c1和c2相同吗? "+e);
运行结果:c1和c2相同吗? false

8)boolean remove(Object o): 移除指定元素,找到返回true。找到第一个满足条件的进行移除,不再继续向下找。

Collection<String> c3 = new ArrayList<>();
c3.add("A");
c3.add("B");
c3.add("C");
c3.add("B");
c3.add("D");
c3.add("D");
boolean d1 = c1.remove("D");
System.out.println("移除元素D: " + d1);
System.out.println("移除D元素后的c3: " + c3);
运行结果:
移除元素D: true
移除D元素后的c3: [A,B,C,B,D]

9)boolean removeAll(Collection<?> c):从集合中删除子集中所具有每个的元素。

Collection<String> c3 = new ArrayList<>();
c3.add("A");
c3.add("B");
c3.add("C");
c3.add("B");
c3.add("D");
c3.add("D");
Collection<String> c4 = new ArrayList<>();
c4.add("B");
c4.add("C");
c3.removeAll(c4);
System.out.println("移除c4后的c3: "+c3);
运行结果:
移除c4后的c3:[A,D,D]

10)boolean retainAll(Collection c):保留两个集合的交集部分。

Collection<String> c3 = new ArrayList<>();
c3.add("A");
c3.add("B");
c3.add("C");
c3.add("B");
c3.add("D");
c3.add("D");
Collection<String> c5 = new ArrayList<>();
c5.add("A");
c5.add("E");
c3.retainAll(c5);
System.out.println("保留c5后的c3: "+c3);
运行结果:[A]

11)void clear():清空集合里的所有元素。

Collection<String> c1 = new ArrayList<String>();
c1.add("A");
c1.add("B");
c1.add("C");
c1.clear();
System.out.println(c1);
System.out.println(c1.size());
运行结果:
[]
0

12)String toString():将集合以字符串的形式输出。

2.3 集合的迭代

1. 集合的迭代,就是指集合的遍历操作。

2. 几乎所有的集合子类型都实现了迭代接口Iterable。

3. 迭代接口提供了多个方式用于迭代。

- boolean hasNext(): 用于判断集合中是否有下一个元素。

可以形象的认为有指针,指针的最初位置在第一个元素前面。

- E next(): 用于返回指针指向的那个元素。然后指针就会移动到下一个元素,为下一次的判断做准备。

- E remove(): 用于删除指针指向的那个元素。

public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection<Integer> c1 = new ArrayList<>();
        c1.add(1);
        c1.add(2);
        c1.add(3);
        c1.add(4);
        c1.add(5);
        /**
         * 使用增强for循环遍历(foreach): 底层原理就是迭代器
         */
        for (Integer i : c1) {
            System.out.println(i+"  ");
        }
        /**
         * 使用迭代器iterator进行遍历
         */
        //第一步:获取要遍历的集合对象的迭代器对象
        Iterator<Integer> it = c1.iterator();
        //第二步:问是否有下一个元素。
        while (it.hasNext()) {
        Integer a = it.next();
        System.out.println("a="+a);
        }
    }
}

 注意在使用迭代器时,不可以调用集合自己的remove(Object o)方法。

            要调用迭代器的remove()方法。

public class CollectionDemo03 {
    public static void main(String[] args) {
        Collection<String> c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("c");
        c1.add("d");
        c1.add("d");
        /**
         * 在使用迭代器时,如果发现有c元素,就将其删除
         */
        Iterator<String> it = c1.iterator();
        while (it.hasNext()) {
            String element = it.next();
            if (element.equals("a")) {
                //使用集合自己的删除方法  会报ConcurrentModificationException,表示不应该修改元素
                //c1.remove(element);
                //可以使用迭代器自己的方法remove
                it.remove();
            }
        }
        System.out.println(c1);
    }
}

三、List子接口

3.1 简介

1. List是一个元素有序并且可以重复的集合。集合中的每个元素都有其对应的顺序索引,从0开始。

2. List允许使用重复的元素,可以通过索引来访问指定位置的集合元素。

3. List默认按元素的添加顺序设置元素的索引。

4. List集合里添加了一些根据索引来操作集合元素的方法。

3.2 ArrayList和LinkedList

这两个类都是List接口的实现类(子类)。

两者在实现上的底层原理对比:

- ArrayList:实现了基于动态数组的数据结构,对象存储在连续的位置上。

- LinkedList:实现了双向链表的数据结构,链表中的每个节点都包含了前一个和后一个元素的引用。

对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。

对于插入和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

基于List接口,两个实现类的方法基本一致。

 Vector:是一个古老的集合, JDK1.0版本时候出现,现在已经被ArrayList和LinkedList替代。

               是线程安全的。

Stack:是一个古老的集合, JDK1.0版本时候出现,模拟栈结构存储数据。是线程安全的。

3.3 常用方法

1)添加元素

1. boolean add(E e):向列表末尾添加指定的元素。

//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
//添加两个元素
adds.add("beijing");
adds.add("shanghai");
System.out.println(adds);
运行结果:
[beijing, shanghai]

2. void add(int index, E element) : 在指定下标处插入元素,下标范围 [0,size()]。

//创建一个ArrayList集合对象
List<String> names = new ArrayList<>();
names.add("michael");
names.add(1,"bob");
System.out.println(names);
运行结果:
[michael,bob]

3. boolean addAll(Collection c):向一个集合的末尾,添加另一个集合。

//创建一个ArrayList集合对象
List<String> names = new ArrayList<>();
//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
names.add("michael");
names.add(1,"bob");
//地址集合添加两个元素
adds.add("beijing");
adds.add("shanghai");
names.addAll(adds);
System.out.println(names);
运行结果:
[michael,bob,beijing, shanghai]

4. boolean addAll(int index,Coolection c):在指定下标处插入一个集合,下标范围[0,size()]。

//创建一个ArrayList集合对象
List<String> names = new ArrayList<>();
//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
names.add("michael");
names.add(1,"bob");
//地址集合添加两个元素
adds.add("beijing");
adds.add("shanghai");
names.addAll(1,adds);
System.out.println(names);
运行结果:
[michael, beijing, shanghai, bob]

2)获取元素

E get(int index): 获取指定索引处的元素。

//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
adds.add("beijing");
adds.add("shanghai");
String ele = adds.get(1);
System.out.println("ele:"+ele);
运行结果:
ele:shanghai

3)查找元素

1.int indexOf(Object o):返回指定元素的第一个下标,如果没有返回-1。

2.int lastIndexOf(Object o): 返回指定元素的最后一个下标,如果没有返回-1。

//创建一个ArrayList集合对象
List<String> names = new ArrayList<>();
//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
names.add("michael");
names.add(1,"bob");
//地址集合添加两个元素
adds.add("beijing");
adds.add("shanghai");
names.addAll(1,adds);
int index = names.indexOf("bob");
System.out.println("index:"+index);
index = names.lastIndexOf("john");
System.out.println("index:"+index);
运行结果:
index:3
index:-1

4)移除元素

1. E remove(int index):移除指定下标处的元素,并返回。

2. boolean remove(Object obj):移除该元素,如果有返回true。

//创建一个LinkedList集合对象
List<String> adds = new LinkedList<>();
adds.add("beijing");
adds.add("shanghai");
boolean f = names.remove("lucy");
System.out.println("f:"+f);
String address = adds.remove(0);
System.out.println("address:"+address);
System.out.println(adds);
运行结果:
f:false
address:beijing
[shanghai]

5)修改元素

E set(int index,E e): 将指定位置上的元素替换成形参e,并返回指定位置上的元素。

List<String> names = new ArrayList<>();
names.add("michael");
names.add("beijing");
names.add("shanghai");
names.add(1,"bob");
String add1 = names.set(2, "深圳");
System.out.println("add1:"+add1);//shanghai
System.out.println("names:"+names); //[michael, beijing, 深圳, bob]
String add2 = names.set(0,names.set(1,"沈阳"));
System.out.println("add2:"+add2);// michael
System.out.println("names:"+names);// [beijing, 沈阳, 深圳, bob]

6)截取子集

List subList(int fromIndex,int endIndex):用于截取集合的一部分,包前不包后

[fromIndex,endIndex),返回截取的子集。

List<Integer> nums = new ArrayList<>();
for (int i = 1; i < 11; i++) {
    nums.add(i);
}
System.out.println(nums);//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
List<Integer> sub = nums.subList(3, 7);
System.out.println(sub);//[4, 5, 6, 7]

3.4 ListIterator

ListIterator是Iterator接口的子接口,继承到了Iterator中所有的方法,同时自己也添加了若干个方法。 允许使用ListIterator在进行元素迭代的时候,对集合中的数据进行修改,或者对集合的长度进行修改。 同时,使用ListIterator还可以进行倒序的迭代。

注意:在进行迭代的过程中,允许修改集合。但是要注意,这个修改集合,只能通过迭代器接口中的方法进行,并不能够通过集合中的方法进行修改。

ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {  
    // 向后指向一位,并返回当前指向的元素   
    String ele = iterator.next();  
    if (ele.equals("Vertu")) {      
        // 在迭代的过程中,删除这个元素      
        iterator.remove();      
        // 在迭代的过程中,添加一个元素(加在当前迭代的这个元素的后面)                          
        iterator.add("HTC");     
        // 在迭代的过程中,对当前迭代的元素进行修改      
        iterator.set("Nokia");   
    }
}

四、Queue子接口

4.1 Queue子接口的简介

Queue:队列,也是Collection的一个子接口。

1. 用来描述一种先进先出(First Input First Output)的数据存储结构。

2. 可以理解为是一种特殊的线性表,只能从一头添加(offer)元素,另一头取出(poll)元素。

3. 对应的实现类LinkedList。

其主要方法如下:

1. boolean offer(E e): 将一个对象添加到队尾,如果添加成功返回true。

2. E poll(): 从队列的头部出来 ,并返回这个元素。

3. E peek(): 查看队列头部的元素。

//创建一个名字队列
Queue<String> names = new LinkedList<>();
//调用offer方法,入队
names.offer("michael");
names.offer("张三");
names.offer("李四");
System.out.println(names);
//查看队列的头部对象
String head = names.peek();
System.out.println(head);
//让头部元素出队
while (names.peek() != null) {
    //如果想要移除头部元素,最后先查看是否有头部元素,如果有再移除。
    String oldHead = names.poll();
    System.out.println(oldHead);
    System.out.println(names);
运行结果:
[michael, 张三, 李四]
michael
michael
[张三, 李四]
张三
[李四]
李四
[]

4.2 Deque

Deque:Queue的子接口,实现了双端队列的存储结构。对应的实现类,还是LinkedList。

双端队列:即从队列的两头都可以入队(offer)和出队(poll)。

提供的方法:

1. offerFirst:从队首进入。

2. offerLast:从队尾进入。

3. pollFirst:从队首出来。

4. pollLast:从队尾出来。

public static void main(String[] args) {
    //创建一个双端队列
    Deque<String> names = new LinkedList<>();
    //添加第一个元素
    names.offer("小明");
    //第二个元素从队列的头部进入
    names.offerFirst("小红");
    //第三元素从队列的尾部进入 offer()  offerLast()
    names.offerLast("小芳");
    System.out.println(names);
    //移除元素
    //移除队列的头部  poll()或pollFirst()
    String head = names.poll();
    System.out.println("head:" +  head);
    //移除队列的尾部
    String tail = names.pollLast();
    System.out.println("tail:" +  tail);
    System.out.println(names);
}
运行结果:
[小红, 小明, 小芳]
head:小红
tail:小芳
[小明]

如果将Deque限制为只能一端出队和入队,那么就可以实现“栈(Stack)”的数据结构,

对于栈来说,入栈被称为push,出栈被称为pop。遵循先进后出(First Input Last Output 简称FILO)

的原则。

使用Deque来模拟栈的存储结构:

public static void main(String[] args) {
    //创建一个存储结构
    Deque<String> stack = new LinkedList<>();
    //从栈的出口进入, 对应的方法push(), 表示将元素推进去。
    stack.push("michael"); //推入到栈的底部
    stack.push("lucy");    //推入,在最底层的上一层
    stack.push("lily");
    stack.push("tom");
    System.out.println(stack);
    //出栈(弹出): 对应的方法pop()  将元素弹出栈结构。
    while (stack.size() > 0) {
        String el = stack.pop();
        System.out.println(el);
    }
}
运行结果:
[tom, lily, lucy, michael]
tom
lily
lucy
michael

五、Set子接口

5.1 Set子接口的简介

Set接口:是Collection接口的子接口。Set接口里的方法都是Collection接口的方法。

1. Set集合用来存储不重复的元素。

添加新元素时的去重原理:

1. 先计算要添加元素的hash值,然后确定哈希表中该值的位置上是否存在元素。

2. 如果不存在,可以添加,如果存在,就需要调用equals进行比较。

3. 如果equals返回true,说明不能在存入了。如果equals返回false,说明可以存入。

注意:每个hash值对应的位置都维护了一个单向链表。Set集合可以存储hash值相同,equals不同的元素。

2. Set集合中的元素是无序的(取出的顺序和存入的顺序无关)。

    元素一旦存入,在存储结构里的顺序就固定了,和存入的先后顺序无关。

public static void main(String[] args) {
    //创建一个Set接口变量
    Set<String> names = new HashSet<String>();
    names.add("michael");
    names.add("lucy");
    names.add("john");
    names.add("tom");
    //每次打印的结果都一样
    System.out.println("names: " + names);
    System.out.println("names: " + names);
}
运行结果:
names: [michael, tom, john, lucy]
names: [michael, tom, john, lucy]

5.2 实现类

5.2.1 HashSet

HashSet是实现Set接口的最最最经典的子类型。大多数时候使用 Set 集合时都使用这个实现类。

底层的存储方式使用的是Hash算法(哈希表),因此具有很好的存取和查找性能。

HashSet的特点:

  • 1) 不能保证元素的排列顺序。

  • 2) HashSet 不是线程安全的。

  • 3) 集合元素可以是 null,且只能是一个。

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。

如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,依然可以添加成功。

public static void main(String[] args) {
    //创建一个Set接口变量
    Set<String> names = new HashSet<String>();
    names.add("michael");
    names.add("lucy");
    names.add("john");
    names.add("tom");
    //每次打印的结果都一样
    System.out.println("names: " + names);
    System.out.println("names: " + names);
    /**
     * 打印一下四个元素的哈希值
     */
    System.out.println("michael".hashCode()%16);
    System.out.println("tom".hashCode()%16);
    System.out.println("john".hashCode()%16);
    System.out.println("lucy".hashCode()%16);
    /*
     * 添加元素michael
     * 因为数据结构中已经存在michael,所以新的michael是添加不进来的。
     */
    names.add("michael");
    System.out.println(names);
    System.out.println(names.size());
}
运行结果:
names: [michael, tom, john, lucy]
names: [michael, tom, john, lucy]
7
2
11
15
[michael, tom, john, lucy]
4

  5.2.2 HashSet的遍历

1. set是无序的,因此不能使用for i 遍历。

2. 使用增强for循环(for each)遍历。

3. 使用迭代器进行遍历。

public static void main(String[] args) {
    Set<Integer> set = new HashSet<Integer>();
    set.add(10);
    set.add(20);
    set.add(30);
    set.add(40);
    set.add(50);
    System.out.println(set);
    for (Integer i : set) {
        System.out.println(i);
    }
    Iterator<Integer> it = set.iterator();
    while (it.hasNext()) {
        Integer i = it.next();
        System.out.println(i);
    }
}
运行结果:
[50, 20, 40, 10, 30]
50
20
40
10
30
50
20
40
10
30

5.2.3 LinkedHashSet

1. LinkedHashSet是HashSet的子类。

2. LinkedHashSet 集合根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护    元素的次序,这使得元素看起来是以插入顺序保存的。

这意味着LinkedHashSet具有以下两种特性:

  • 有序性(Order):LinkedHashSet会保持元素的插入顺序,即元素被添加到集合中的顺序就是它们在集合中的顺序。
  • 唯一性(Uniqueness):与 HashSet 一样,LinkedHashSet 保证元素的唯一性,不允许重复元素。

3. LinkedHashSet 性能插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

4. LinkedHashSet 不允许集合元素重复。

public static void main(String[] args) {
    Set<String> set = new LinkedHashSet<String>();
    set.add("a");
    set.add("b");
    set.add("c");
    set.add("bob");
    System.out.println(set);
    //使用迭代器遍历
    Iterator<String> iterator = set.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}
运行结果:
[a, b, c, bob]
a
b
c
bob

5.2.4 TreeSet

1. TreeSet 是SortedSet接口的实现类。

2. TreeSet集合的元素是有序的,即内部维护了一个二叉树算法的排序方式。

3. TreeSet集合里的元素也不能重复。

4. TreeSet默认是升序排序,如果想要改变默认默认的排序方式,可以自定义一个比较器,传入构造器中。

public static void main(String[] args) {
    Set<String> set = new TreeSet<String>();
    set.add("a");
    set.add("h");
    set.add("b");
    set.add("f");
    set.add("d");
    set.add("bob");
    set.add("bab");
    System.out.println(set);
    Comparator<Integer> c1 = new Comparator<Integer>(){
        public int compare (Integer o1,Integer o2){
            return o2 - o1;
        }
    };
    Set<Integer> nums = new TreeSet<>(c1);
    nums.add(10);
    nums.add(1);
    nums.add(20);
    nums.add(2);
    System.out.println(nums);
}
运行结果:
[a, b, bab, bob, d, f, h]
[20, 10, 2, 1]

5.2.5 常用方法

Set接口中没有新增的方法,所有的方法都是从父接口Collection中继承到的。

5.2.6 Set的去重原理

1)HashSet 、LinkedHashSet

1. 先计算要添加元素的hash值,然后确定哈希表中该值的位置上是否存在元素。

2. 如果不存在,可以添加,如果存在,就需要调用equals进行比较。

3. 如果equals返回true,说明不能在存入了。如果equals返回false,说明可以存入。

2) TreeSet

如果元素的比较规则中,两个对象的比较结果是0,则视为是同一个元素,去重。

六、List排序

6.1 Comparable接口

自定义的类,如果想要排序,不管是在数组中,还是在集合中。要求元素必须是Comparable的子类型,因为底层需要调用Comparable的CompareTo方法。所以,自定义的类,如果想要排序,那么必须实现Comparable接口,以及重写CompareTo方法。

6.2 工具类提供的排序方法

集合工具类:Collections。

1. 集合工具类和数组工具类一样,都提供了很多常用的方法,来操作集合对象。

 - void sort(List<E> list): 对集合元素进行升序排序。

 - void sort(<List<E>,Comparator<E> c): 改变集合元素的排序规则。

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    //将10个随机数存入到ArrayList集合中
    for (int i = 0; i < 10; i++) {
        list.add((int)(Math.random()*100));
    }
    System.out.println(list);
    /**
     * 利用工具类里的排序方法,默认升序排序。
     */
    Collections.sort(list);
    System.out.println(list);
    /**
     * 由于集合工具类里的sort方法是默认升序排序,
     * 现在想要重新指定排序规则:降序。
     * 那么就需要调用另外一个方法sort(<List<E>,Comparator<E> c)
     */
    Comparator<Integer> c = new Comparator<Integer>() {
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    };
    Collections.sort(list, c);
    System.out.println(list);
}
运行结果:
[78, 84, 67, 92, 28, 83, 53, 52, 43, 77]
[28, 43, 52, 53, 67, 77, 78, 83, 84, 92]
[92, 84, 83, 78, 77, 67, 53, 52, 43, 28]

6.3 Compatator比较器接口

如果想要重新指定排序方式,不应该修改Comparable接口里的CompareTo方法的逻辑 ,而是应该使用Comparator比较器接口,来定义一个新的比较规则。调用集合或者数组的相关重载方法,可以传入一个比较器的这样的方法,进行新的排序。

6.4 Collections

Collections 是一个操作 Set、List 和 Map 等集合的工具类,提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

1)排序操作

1. reverse(List list):反转List中的元素顺序。

List<String> names = new LinkedList<String>();
names.add("michael");
names.add("peter");
names.add("mark");
names.add("john");
names.add("jane");
//打印集合,为添加元素时的顺序
System.out.println(names);
//调用集合工具类里的反转方法,将集合元素颠倒过来。
Collections.reverse(names);
System.out.println("反转后的结果:"+names);
运行结果:
[michael, peter, mark, john, jane]
反转后的结果:[jane, john, mark, peter, michael]

2. shuffle(List list):对List集合元素进行随机排序。

//将集合里的元素顺序打乱
Collections.shuffle(names);

3.  swap(List list, int i, int j):将指定List集合中的i处元素和j处元素进行交换。

//将第二个和倒数第二个做一下交换
Collections.swap(names, 1, names.size()-2);

4.  sort(List list):将List集合里的元素升序排序。

5. sort(List list,Comparator c):根据自定义的比较规则,对List集合里的元素进行排序。

2)查找、替换

1. Object max(Collection c):根据元素的自然排序,返回给定集合中的最大元素。

2. Object max(Collection c,Comparator c):根据自定义的规则进行排序。

注意:上述两种方法,返回的都是排序后的最后一个元素

//调用max方法: 底层使用的是自然排序,找到最大的
String ele = Collections.max(names);

3. Object max(Collection c):根据元素的自然排序,返回给定集合中的最小元素。

4. Object max(Collection c,Comparator c):根据自定义的规则进行排序。

注意:上述两种方法,返回的都是排序后的第一个元素

//调用min方法: 底层使用的是自然排序,找到最小的。
String ele2 = Collections.min(names);

5.  int frequency(Collection c,Object o):返回指定集合中指定元素出现的次数。

//调用frequency方法,找出michael在names集合里出现的次数。
int count = Collections.frequency(names,"michael");

 6. boolean replaceAll(List list, Object oldValue,Object newValue):使用新值替换List集合对象里所有的旧值。

//将集合里的所有michael替换成张三。
Collections.replaceAll(names,"michael","张三");

 6.5 数组与集合之间的转换

6.5.1 集合转数组

1. Object[] toArray(): Object[] 一旦想要使用数组中元素的自己的方法和属性。

                                还需要强转: 将数组中的元素转成集合指定的类型。

List<String> names = new ArrayList<String>();
names.add("michael");
names.add("peter");
names.add("mark");
Object[] arr1 = names.toArray();
Object obj = arr1[1];
if(obj instanceof String ) {
    String str = (String) obj;
    char ch = str.charAt(1);
}

 2. T[] toArray(T[] a):将集合转成数组。 只需要传入一个元素类型的数组对象,就可以返回元素类型的数组。

List<String> names = new ArrayList<String>();
names.add("michael");
names.add("peter");
names.add("mark");
String[] arr = new String[0];
String[] arr2 = names.toArray(arr);

6.5.2 数组转集合

1. 数组转成的集合对象,不能修改长度。

2. 如果想要进行增删操作,需要放入另外一个空集合里。

调用Arrays.asList方法,将数组转成集合。

String[] names = new String[5];
names[0] = "michael";
names[1] = "tom";
names[2] = "lucy";
names[3] = "lily";
names[4] = "john";
List<String> list = Arrays.asList(names);
/**
 * 向集合中添加新元素,张三和李四,然后将张三和第一个元素交换位置。
 */
List<String> list1 = new ArrayList<>(list);
list1.add("张三");
list1.add("李四");
for (int i = 0; i < list1.size(); i++) {
    if (list1.get(i).equals("张三")) {
        Collections.swap(list1,0,i);
    }
}

七、Map接口

7.1 Map简介

1. Map接口,是集合框架中的另一个父接口。

2. Map存储的数据的特点: 一对一的关系映射, 称之为Key-Value-Pair(键值对)。

3. Map接口最常用的两个子类,是HashMap和TreeMap。

    - HashMap: 底层使用了Hash表和红黑树的数据结构(jdk1.8以前使用的是hash表+单向链表)。

    - TreeMap: 底层使用了二叉树。

4. Map的Key不能重复,但是可以为null。value可以重复。

5. Map的Key可以理解为是value的索引,总能通过一个Key找到一个具体的value。

6. Map里的元素也是无序的(在结构中的(存储)顺序和存入顺序无关)。

7. key和value必须是引用类型的数据。

7.2 常用方法

1. V put(Key key,Value value):添加元素,像Map集合添加一个键值对。

  注意:key值可以为null,但是只能有一个

 小贴士:put方法,如果添加key相同,value不同的元素,新的value会覆盖掉原来的value。

//创建一个Map集合(散列表)
Map<String,Integer> map = new HashMap<>();
map.put("张三", 100);
map.put("李四", 98);
map.put("王五", 97);
map.put("赵六", 99);
System.out.println(map);
map.put("张三",60);
System.out.println(map);
map.put(null,0);
map.put(null,10);
System.out.println(map);
运行结果:
{李四=98, 张三=100, 王五=97, 赵六=99}
{李四=98, 张三=60, 王五=97, 赵六=99}
{null=10, 李四=98, 张三=60, 王五=97, 赵六=99}

2. V get(Object key):返回指定键所映射的值。

注意:如果指定键key不存在,返回null。

Integer score = map.get("张三");
System.out.println("张三的成绩:" + score);
//取出小八的成绩。因为key不存在,所以返回null。
Integer s1 = map.get("小八");
System.out.println("小八的成绩:"+s1);
运行结果:
张三的成绩:60
小八的成绩:null

3. boolean  isEmpty():判断集合是否为空。

boolean a = map.isEmpty();

4. boolean containsKey (Object o):判断集合是否包含指定的key。

boolean b = map.containsKey("张三");

5. boolean containsValue(Object o):判断集合是否包含指定的Value。

boolean c = map.containsValue(96);

6. V remove(K k):通过key移除一个键值对。

course.remove("李四");

7. int size():查看Map集合里键值对的个数。

int count = map.size();

8. void clear():清空所有键值对。

map.clear();

7.3 Map的遍历

1)第一种方式:使用keySet():返回所有的key的Set集合形式。

Set<String> keys = scores.keySet();
for(String key:keys){
    //通过key获取value
    Integer value = scores.get(key);
    System.out.println(key+"="+value);
}

2)第二种方式:使用entrySet(): 返回Entry对象的Set集合。

                            Entry: 是Map的内部类,Entry对象就是封装了一个键值对。

Set<Map.Entry<String,Integer>> es = scores.entrySet();
for(Map.Entry<String,Integer> entry:es){
    //每一个entry都是一个键值对
    //System.out.println(entry);
    /**
     * Entry类型提供了getKey()和getValue()方法
     */
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key+"="+value);
}

3)第三种方式:使用values():返回所有value的Collection集合形式。

Collection<Integer> values = scores.values();
for(Integer value:values){
    System.out.println(value);
}

7.4 HashMap的实现原理

7.4.1 原理

 HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的hash冲突。

图中,紫色部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中。

7.4.2 装载因子及其HashMap优化

capacity:容量,hash表里bucket(桶)的数量,也就是散列数组大小。

initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量为16,也可以使用特定容量。

size:大小,当前散列表中存储数据的数量。

load factor:加载因子,默认值0.75也就是75%,当向散列表增加数据时,如果size/capacity的值大于loadfactor,则发生扩容并且重新散列(rebash)。

性能优化:加载因子较小时,散列查找性能会提高,但是也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量,减少rehash能提供性能。

7.5 HashMap与HashTable

HashMap和HashTable是Map接口的两个典型实现类。

区别:

1. HashTable是一个古老的Map实现类,不建议使用。

2. HashTable是一个线程安全Map实现类,但HashMap是线程不安全的。

3. HashTable不允许使用null作为key和value,而HashMap可以。

与 HashSet 集合不能保证元素的顺序的顺序一样,HashTable 、HashMap 也不能保证其中 key-value 对的顺序。

7.6 LinkedHashMap

LinkedHashMap是HashMap的子类。

使用链表维护了元素的插入顺序,迭代顺序与键值对的插入顺序一致。

7.7 TreeMap

1. 使用二叉树对key进行排序,来维护整个集合的键值对的顺序。

2. 默认升序。可以通过比较器进行自定义排序。

public static void main(String[] args) {
    Comparator c1 = new Comparator<String>() {
        public int compare(String o1, String o2) {
            return -o1.compareTo(o2);//按照key的值降序
        }
    };
    Map<String,Integer> map = new TreeMap<>(c1);
    map.put("A", 100);
    map.put("a", 90);
    map.put("b", 80);
    map.put("C", 90);
    System.out.println(map);
}
运行结果:
{b=80, a=90, C=90, A=100}

7.8 Properties

1. 是HashTable的子类型,比较常用,一般用于加载配置文件里的KEY和VALUE。

2. 因为配置文件里都是字符串,因此properties里面的KEY和VALUE也都是String类型。

3. 该对象的key和value都不可以为null。

1)Properties的遍历

//创建一个配置文件属性对象
Properties prop = new Properties();
//添加几个key和value
prop.setProperty("url","jdbc:mysql://localhost:3306/mydb");
prop.setProperty("username","root");
prop.setProperty("password","root");
prop.setProperty("driver","com.mysql.jdbc.Driver");
for (Map.Entry<Object,Object> entry : prop.entrySet()) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

2)getProperty(Key key)

通过指定的key,获取对应的value值,如果key不存在,返回null。

String url = prop.getProperty("url");
System.out.println("url的值是:  " + url);
运行结果:
url的值是:  jdbc:mysql://localhost:3306/mydb

3)getProperty(Key key,Value defaultValue)

通过指定的key,获取对应的value值,如果该key不存在,就返回默认值defaultValue。

String p1 = prop.getProperty("school","哈尔滨工业大学");
System.out.println(p1);
运行结果:
哈尔滨工业大学

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在线图片编辑网站推荐(图片压缩)

&#x1f525;发现神器&#xff01;「可乐改图」——一站式在线图片编辑平台&#xff0c;让工作更高效&#xff01;&#x1f680; 大家好&#xff01;今天我要给大家安利一个我最近发现的宝藏工具——「可乐改图」&#xff0c;一个集多功能于一身的在线图片编辑平台&#xff0…

前端(Vue)动态换肤的通用解决方案及原理分析(2)

文章目录 动态换肤的主题解决方案总结处理 第三方( element-plus )主题变更原理与步骤分析**实现原理**实现步骤处理 element-plus 主题变更补充 > 步骤 2&#xff1a;获取当前 element-plus 的默认样式表&#xff0c;并且把需要进行替换的色值打上标记补充>步骤 3&#…

Android 手机恢复出厂设置后,还能恢复其中数据吗?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份、网络及终端数据安全等解决方案与服务。 同时&#xff0c;鸿萌是众多国际主流数据恢复软件的授权代理商&#xff0c;为专业用户提供正版的数据恢复软件。 对于 A…

网络版计算器(理解协议与序列化与反序列化)

一、理解协议 在网络层面&#xff0c;协议&#xff08;Protocol&#xff09;是一组规则、标准或约定&#xff0c;它们定义了在网络环境中&#xff0c;计算机、服务器、路由器、交换机等网络设备之间如何相互通信和交换信息。这些规则涵盖了数据格式、数据交换的顺序、速度、以及…

调研-音视频

音视频 基础概念主要内容音频基础概念音频量化过程音频压缩技术视频基础概念视频bug视频编码H264视频像素格式YUVRGB参考文献基础概念 ● 实时音视频应用环节 ○ 采集、编码、前后处理、传输、解码、缓冲、渲染等很多环节。 主要内容 音频 基础概念 三要素:音调(音频)、…

阿里云注册、认证、短信资质、签名、模板申请过程

一、帐号注册 输入“帐号密码注册”中的相关信息即可。 手机号是必须的&#xff0c;先确定好手机号。 正常的可以直接注册成功的。 二、实名认证 注册成功之后&#xff0c;就可以点击上述的“快速实名认证”。 这次选择的是“企业认证”。 有几种方式&#xff0c;如下&#x…

学习嵌入式第二十八天

有名管道 在C语言中&#xff0c;有名管道&#xff08;Named Pipe&#xff09;是一种特殊的文件类型&#xff0c;它允许进程间通信。有名管道与匿名管道&#xff08;Anonymous Pipe&#xff09;不同&#xff0c;它在文件系统中有一个路径名&#xff0c;因此可以被多个进程访问。…

项目实战-Linux部署-安装jdk以及shell脚本检查jdk

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

STM32之MPU6050实战

MPU6050 MPU6050是一个6轴姿态传感器&#xff0c;可以测量芯片自身X、Y、Z轴的加速度、角速度参数&#xff0c;通过数据融合&#xff0c;可进一步得到姿态角&#xff0c;常应用于平衡车、飞行器等需要检测自身姿态的场景 3轴加速度计&#xff08;Accelerometer&#xff09;&a…

Python从0到100(五十二):逻辑回归及鸢尾花数据集预测

逻辑回归是⼀种⽤于解决⼆分类问题的监督学习算法&#xff0c;其基本原理是使⽤ 逻辑函数&#xff08;也称为Sigmoid函数&#xff09; 来建模 因变量&#xff08;输出&#xff09;与⾃变量&#xff08;输⼊&#xff09;之间的概率关系。逻辑回归的⽬标是估计某个事件发⽣的概率…

YOLOV8网络结构|搞懂Backbone-SPPF

SPPF SPP衍生而来。 因为速度快&#xff0c;所以是SPPF-Fast CONV 3个Maxpool串联 Concat 最后又Conv

QT 控件使用案例

常用控件 表单 按钮 Push Button 命令按钮。Tool Button&#xff1a;工具按钮。Radio Button&#xff1a;单选按钮。Check Box&#xff1a;复选框按钮。Command Link Button&#xff1a;命令链接按钮。Dialog Button Box&#xff1a;按钮盒。 容器组控件(Containers) Group Box…

JavaEE 的相关知识点(一)

一、过滤器 过滤器&#xff08;Filter&#xff09;是一个用于对请求和响应进行预处理的组件。过滤器可以在 Java Servlet 规范中使用&#xff0c;通常用于执行一些通用的任务 1、过滤器的作用 过滤器是一种javaEE规范中定义的一种技术&#xff0c;可以让请求达到目标servlet之…

Open3D 格网法计算点云的占地面积

目录 一、概述 1.1原理 1.2实现步骤 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2数据显示 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博客 一、概…

林小茶 C语言程序设计 8.48.58.6答案

【8.4】用结构体表示日期&#xff0c;编写程序计算北京奥运会倒计时的天数并输出&#xff08;2008年8月8日北京奥运会开幕&#xff0c;输入的日期范围是2008年1月1日-2008年8月7日&#xff09;。 #include<stdio.h> struct Date{int year;int month;int day; }; int mai…

硬件电路仿真-LTspice官方软件使用-运放电路仿真实战

文章目录 一&#xff1a;LTspice简介1.1 推荐先简单运用1.2 课程配套资料1.3 仿真过程1.4 SPICE模型1.5 LTSPICE工具栏和快捷键1.6 LTSPICE数量级 二&#xff1a;基本功能&#xff08;探索功能如何使用&#xff09;2.1 瞬态分析(.tran)2.2 交流分析&#xff08;.ac&#xff09;…

基于Springboot3 +vue2的民宿酒店预订系统

这个一个大数据库课程设计&#xff0c;也是计算机软件课程设计大作业&#xff0c;Springboot vue民宿酒店预订系统 本系统是采用Springboot3 vue2的酒店预订系统 &#xff0c;数据库mysql ,用户权限分为系统管理员&#xff0c;客房操作人员、和 普通用户&#xff08;游客&…

如何在Python中使用情感分析API

情感分析 API 服务是一种借助人工智能技术的工具&#xff0c;能够自动识别并衡量文本数据&#xff08;像社交媒体的帖子、产品的评论、新闻文章等等&#xff09;所蕴含的情感色彩。在本文里&#xff0c;我们会一同探讨怎样在 Python 中集成情感分析 API &#xff0c;并且展示它…

【Python机器学习】利用SVD简化数据——示例:菜肴推荐引擎

现在&#xff0c;构建一个推荐引擎&#xff0c;该推荐引擎关注的是餐馆食物的推荐。假设一个人决定外出吃饭&#xff0c;但并不知道去哪吃什么&#xff0c;我们这个推荐系统就可以帮他做到这两点。 首先我们构建一个基本的推荐引擎&#xff0c;它能够寻找用户没有尝过的菜肴&a…

C++(11)类语法分析(2)

C(10)之类语法分析(2) Author: Once Day Date: 2024年8月17日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客 …