Java集合(一)

简介: Java集合(一)

Java集合

集合的概念

在java中,集合是一个名词而不是动词,集合是一种存储数据的容器。

如果只是为了容纳数据,可以使用数组。为什么有数组了还需要集合来存储数据呢?

  • 数组使用时要确定元素个数,当使用场景中的元素个数不确定时,使用数组不太方便。

集合指的是一系列框架,在java.util中。包含了集合一系列的接口和类。

对不确定个数的有关系的数据进行相同的逻辑处理时,我们使用集合来操作更好。

根据数据的不同,java中的集合分为两种类型:

  • 单一数据体系:Collection接口定义了相关规范
  • 成对出现的数据体系:所谓的成对,是值一对数据相关联,可根据第一个数据关联到第二个数据。也称之为键值对数据("kevin",20)->(key,value)。比如Map接口就定义了这种规则。

常用接口和类

Collection接口:

常用的子接口

  • List:按照插入顺序保存数据,数据可以重复。
    • 主要的具体实现类为:ArrayList,LinkedList
  • Set:集,无序保存。数据不能重复。
    • 主要的具体实现类为:HashSet
  • Queue:队列
    • 主要的具体实现类为:ArrayBlockingQueue

Map接口:

  • 主要的具体实现类为:HashMap,HashTable

List

ArrayList

List:表示列表,清单。在清单中我们是先取出先插入的,所以list也是按照插入顺序读取,第一个插入,读取时也是第一个。并且它可以存储重复的数据。

Array:表示数组,序列。

在ArrayList这个集合类的内部就是使用数组来存储数据的。

ArrayList对象的创建:

ArrayList arrayList = new ArrayList();

其中,有三种方式创建一个ArrayList:

  • 不传参数,直接new。底层会创建一个空数组
  • 传一个int类型的参数,用于设定底层数组的长度
  • 传一个Collection集合类型的对象,用于将其他集合放置在当前集合中

我们来打印这个ArrayList对象:

ArrayList arrayList = new ArrayList();
System.out.println(arrayList);// []

返回的集合对象中的数据(当前为空)

集合对象的基本操作

增加数据

我们使用add方法向集合添加数据:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qina");
arrayList.add("kun");
System.out.println(arrayList);// [kevin, qina, kun]

对于一个没有传参new出的对象,它底层的数组长度会被自动设置为10。

那如果我们的数据超出了底层的数组长度会怎样呢?

为了方便,我们就声明一个底层数组长度为3的集合:

ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qina");
arrayList.add("kun");
arrayList.add("yes");
System.out.println(arrayList);// [kevin, qina, kun, yes]

我们看到,依然会进行添加数据。并且也没有报错,那这是为什么呢?

原来,在底层,遇到元素个数超出数组长度时。会创建一个更大的数组,并且将索引的内存空间指向原来的对象,然后再进行添加。这个操作叫扩容。而原来的数组就不会被使用了。

image-20230109205656166

获取数据

使用get方法获取数据,传入参数为索引。使用size()方法来获取集合的元素个数

ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");

System.out.println(arrayList.size());// 4
System.out.println(arrayList.get(1));// qian

使用for循环来遍历集合:

for (int i = 0; i < arrayList.size(); i++) {
    System.out.println(arrayList.get(i));
}

如果不关心数据的位置,我们可以使用forin来遍历(按顺序遍历):

  • for(类型 循环对象:集合){}
for (Object o : arrayList) {
    System.out.println(o);
}

修改数据

使用set()方法修改数据,传入两个参数:

  • 第一个参数:下标(索引)
  • 第二个参数:修改的值

方法会返回结果,返回值为更新前的值:

ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");

Object chage = arrayList.set(3, "no");
arrayList.set(0, 3.0);
System.out.println(chage);// yes
System.out.println(arrayList);// [3.0, qian, kun, no]

删除数据

使用remove()方法可以删除集合的数据,传入参数为数据的位置(索引):

方法会返回结果,返回值为要删除的值

ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");

Object o = arrayList.remove(1);
System.out.println(o);//qian
System.out.println(arrayList.size());// 3
System.out.println(arrayList);// [kevin, kun, yes]

ArrayList常用方法

add()传入两个参数时,可以用于向集合指定位置插入数据:

  • 第一个参数:索引
  • 第二个参数:添加的数据
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");

arrayList.add(0, "demo");
System.out.println(arrayList);// [demo, kevin, qian]

当插入数据时,之前该位置的数据会与后面的数据一起向后移。

使用clear()方法来清空集合中的数据:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");

System.out.println(arrayList.size());// 2
arrayList.clear();
System.out.println(arrayList);// []
System.out.println(arrayList.size());// 0

使用removeAll()来删除集合中的指定数据(如果移除了返回true,反之为false):

参数类型是一个集合,可以使用Collections.singleton()来转为一个集合。

使用addAll()方法可以将一个集合的所有数据添加到该集合中(同样可以传两个参数)。

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

ArrayList list2 = new ArrayList();
list2.add("ok");
list2.add("nice");

arrayList.addAll(list2);

arrayList.removeAll(Collections.singleton("qian"));
System.out.println(arrayList);//[kevin, ok, nice]

boolean b = arrayList.removeAll(list2);
boolean b1 = arrayList.removeAll(Collections.singleton("lll"));
System.out.println(b);//true
System.out.println(b1);//false
System.out.println(arrayList);//[kevin]

使用isEmpty()方法来判断集合是否为空:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

ArrayList list2 = new ArrayList();
System.out.println(arrayList.isEmpty());// false
System.out.println(list2.isEmpty());// true

使用contains()方法来判断集合中是否有这个数据:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

System.out.println(arrayList.contains("kevin"));// true
System.out.println(arrayList.contains("kun"));// false

使用indexOf()方法和lastIndexOf()找到第一次出现和最后一次出现该数据的位置

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

System.out.println(arrayList.indexOf("qian"));// 1
System.out.println(arrayList.lastIndexOf("qian"));// 3

集合转数组:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

Object[] objects = arrayList.toArray();
System.out.println(objects[1]);//qian

复制一份集合:

ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");

Object clone = arrayList.clone();
System.out.println(clone);// [kevin, qian, qian, qian]

LinkedList

LinkedList是一种链表结构

LinkedList list = new LinkedList();
list.addFirst("kevin");
System.out.println(list.getFirst());// kevin
System.out.println(list.getLast());// kevin
System.out.println(list);// [kevin]

取第一个数据和最后一个:

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");

System.out.println(list.getFirst());// qian
System.out.println(list.getLast());// kevin
System.out.println(list);// [qian, kevin]

添加数据(add默认添加到最后,两个参数为插入该索引位置):

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");

System.out.println(list);// [qian, kun, kevin]

get方法取数据

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");

System.out.println(list.get(1));// kun

使用for循环遍历

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");

for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
// qian
// kun
// kevin

使用forin遍历:

for (Object o : list) {
    System.out.println(o);
}

set方法修改数据

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");

list.set(0, "kkkevin");
System.out.println(list.getFirst());// kkkevin

remove方法移除集合中的传入第一个数据:

LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
list.addLast("kevin");

list.remove("kevin");
System.out.println(list);// [qian, kun, kevin]

LinkedList常用方法

除了上面的一些方法外,它还有一些api我们经常使用:

push()方法用于向开头添加数据,类似addFirst。

LinkedList list = new LinkedList();

list.push("kevin");
list.push("qian");
list.push("kun");
list.add("yt");

System.out.println(list);//[kun, qian, kevin, yt]

element()方法用于取第一个数据,类似getFirst。

System.out.println(list.element());//kun

pop用于弹出数据,弹出第一个数据,返回值也是弹出的数据。

LinkedList list = new LinkedList();

list.push("kevin");
list.push("qian");
list.push("kun");
list.add("yt");

System.out.println(list);//[kun, qian, kevin, yt]

System.out.println(list.pop());// kun
System.out.println(list);// [qian, kevin, yt]

泛型

由于多态性,集合在默认情况下下使用的时候非常不方便,因为数据的默认类型都是Object:

ArrayList list = new ArrayList();
list.add(new Person());
list.add(new Person());
Object o = list.get(1);
o.personName();// 无法解析 'Object' 中的方法 'personName'

Person类:

class Person {
    public void personName() {
        System.out.println("person...");
    }
}

如果要使用,就必须使用强制类型转换。非常不方便,而且可能出现类型转换错误的异常。

Person o = (Person) list.get(1);
o.personName();// person...

这时候我们可以使用泛型,我们传入的数据都是同一个类型:

ArrayList<Person> list = new ArrayList();
list.add(new Person());
Person person = list.get(0);
person.personName();// person...

类型存在多态的使用,而泛型是没有多态的。

public class JavaCollection_05 {
    public static void main(String[] args) {
        Contains<User5> contains = new Contains();
        test(contains);// 需要的类型:Contains<Object> 提供的类型:Contains<User5>
    }

    public static void test(Contains<Object> contains) {
        System.out.println(contains);
    }
}

class Contains<C> {
    public C data;
}

class User5 {
}

比较器

集合提供了sort方法来进行排序,传入的是一个实现了比较器接口的类的实例对象

import java.util.ArrayList;
import java.util.Comparator;

public class JavaCollection_06 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(2);
        list.add(1);
        list.add(3);
        list.add(0);

        list.sort(new NumberComparator());
        System.out.println(list);// [0, 1, 2, 3]
    }
}

class NumberComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;// 升序
    }
}

其中,compare方法中:

  • 参数1>参数2,返回值为正数,表示升序
  • 参数1<参数,返回值为负,表示降序
  • 参数1==参数2,返回值为0。

官方解释:compare方法比较其两个顺序参数。返回负整数、零或正整数,因为第一个参数小于、等于或大于第二个参数。

ArrayList与LinkedList比较

添加第一条数据,LinkedList更快

image-20230110191430567

添加后面的数据,ArrayList更快

但是,当ArrayList需要扩容时,LinkedList更快。

而插入数据,LinkedList更快。

image-20230110193639330

由于LinkedList没有索引的概念(在LinkedList中叫顺序),在LinkedList中使用索引实际上还是在按着顺序找。所以使用索引查找时,ArrayList更快。

如果不使用索引查找,两个集合的查找没有本质区别。

相关文章
|
28天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
37 6
|
28天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
38 3
|
28天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
30天前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
28 3
|
8天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
16 2
|
7天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
12天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
12天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
12天前
|
Java 开发者
|
24天前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
52 5