Java学习笔记之集合(上)
2021/4/12 20:27:26
本文主要是介绍Java学习笔记之集合(上),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 集合概述
- 为什么需要集合
- Java中的集合结构
- Conection接口
- List 接口
- List接口介绍
- List接口的实现类
- ArrayList
- Vector
- LinkedList
- List接口的实现类
- List接口介绍
- Set接口
- HashSet
- TreeSet
- 关于重复元素的说明
- List 接口
集合概述
为什么需要集合
- 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象进行操作,就需要对对象进行存储
- 虽然我们可以使用
Array
存储对象,但这种存储对象的方式也有一些弊端。- 数组初始化以后,长度就不可变了,不便于扩展
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高
- 同时无法直接获取存储元素的个数
- 数组存储的数据是有序的、可以重复的。即存储数据的特点单一
Java中的集合结构
Conection接口
Collection
接口用于存储单列数据的最大操作接口,定义了存取一组对象的方法的集合。
该接口中定义了以下方法:
No. | 方法名称 | 描述 |
---|---|---|
1 | public boolean add(E e) | 向集合中插入一个元素 |
2 | public boolean addAll(Collection c) | 向集合中插入一组元素 |
3 | public void clear() | 清空集合中的元素 |
4 | public boolean contains(Object o) | 查找一个元素是否存在 |
5 | public boolean containsAll(Collection c) | 查找一组元素是否存在 |
6 | public boolean isEmpty() | 判断集合是否为空 |
7 | public Iterator iterator() | 实例化该集合对应的迭代器 |
8 | public boolean remove(Object o) | 从集合中删除一个对象 |
9 | boolean removeAll(Collection c) | 从集合中删除一组对象 |
10 | boolean retainAll(Collection c) | 判断是否没有指定的集合 |
11 | public int size() | 求出集合中元素的个数 |
12 | public Object[] toArray() | 以对象数组的形式返回集合中的全部内容 |
13 | T[] toArray(T[] a) | 指定操作的泛型类型,并把内容返回 |
14 | public boolean equals(Object o) | 从 Object 类中覆写而来 |
15 | public int hashCode() | 从 Object 类中覆写而来 |
注意:
JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如: Set和List)实现
在 Java5 之前, Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理; 从 JDK 5.0 增加了泛型以后, Java 集合可以记住容器中对象的数据类型
List 接口
了解Collection接口之后,我们先来看看Collection接口中的子类List
。
List接口介绍
List
见名知意,即列表。那什么是列表呢?列表就相当于以前学习的数组,和数组不同的是列表被封装成了一个对象,增删改查操作依赖于其中的方法实现,同时列表对象内部自带动态增容,使我们无需关心其容量。
其中在Collection接口的基础上新增和重载了以下方法:
No. | 方法名称 | 描述 |
---|---|---|
1 | public void add(int index,E element) | 在指定位置处增加元素 |
2 | boolean addAll(int index,Collection c) | 在指定位置处增加一组元素 |
3 | public E get(int index) | 根据索引位置取出每一个元素 |
4 | public int indexOf(Object o) | 根据对象查找指定的位置,找不到返回-1 |
5 | public int lastIndexOf(Object o) | 从后面向前查找位置,找不到返回-1 |
6 | public ListIterator listIterator() | 返回该List对象对应的迭代器 |
7 | public ListIterator listIterator(int index) | 返回从指定位置的List对象对应的迭代器 |
8 | public E remove(int index) | 删除指定位置的内容 |
9 | public E set(int index,E element) | 修改指定位置的内容 |
10 | List subList(int fromIndex,int toIndex) | 返回子集合 |
List接口的实现类
ArrayList
ArrayList
是对象引用的一个”变长”数组
基本操作示例:
public class ArrayListDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 实例化List对象,并指定泛型类型 list.add("hello"); // 增加内容,此方法从Collection接口继承而来 list.add(0, "world");// 增加内容,此方法是List接口单独定义的 System.out.println("集合中的内容是:" + list);// 打印list对象调用toString()方法 list.remove(1); // 根据索引删除内容,此方法是List接口单独定义的 list.remove("world");// 删除指定的对象 System.out.print("集合中删除后的内容是:" + list); for (int x = 0; x < list.size(); x++) { // size()方法从Collection接口继承而来 System.out.print(list.get(x) + "、"); // 此方法是List接口单独定义的 } } }
控制台输出如下:
Vector
Vector 是一个古老的集合, JDK1.0就有了,大多数操作与ArrayList相同。
- 示例:将
ArrayList
示例中的ArrayList
改为Vector
即可 - 与
ArrayList
的区别如下:Vector
是线程安全的,性能较低;ArrayList
是非线程安全的,性能较高ArrayList
支持 Iterator、ListIterator 输出;Vector
除了支持 Iterator、ListIterator 输出,还支持Enumeration 输出
LinkedList
与ArrayList
大部分操作相同,但是此类也是 Queue 接口的子类,Queue 接口定义了如下的方法:
No. | 方法名称 | 描述 |
---|---|---|
1 | public boolean add(E e) | 增加元素,如果有容量限制,并且已满,则抛出异常 |
2 | public E element() | 取得元素,但是不删除当前元素,如果队列为空则抛出异常 |
3 | boolean offer(E e) | 添加,如果有容量限制,并且已满,只是无法添加,但不抛出异常,返回 false |
4 | E peek() | 取得头元素,但是不删除,如果队列为空,则返回null |
5 | E poll() | 取得头元素,但是删除, 如果队列为空,则返回 null |
6 | E remove() | 删除当前元素, 如果对列为空则抛出异常 |
- 继承
List
示例将ArrayList
示例中的ArrayList
改为LinkedList
即可 - 继承队列
Queue
示例:
public class LinkedListDemo { public static void main(String[] args) { Queue<String> queue = new LinkedList<String>(); queue.add("A"); queue.add("B"); queue.add("C"); String element = queue.element(); System.out.println("element:" + element); String peek = queue.peek(); System.out.println("peek:" + peek); //把queue的大小先取出来,否则每循环一次,移除一个元素,就少一个元素,那么queue.size()在变小,就不能循环queue.size()次了。 int len=queue.size(); for (int x = 0; x <len; x++) { System.out.println(queue.poll()); } System.out.println(queue); } }
控制台输出:
- 与
ArrayList
的区别如下:ArrayList
底层是使用 Array 数组实现,LinkedList
底层是使用链表实现- 由于
LinkedList
底层使用链表实现,对于频繁的插入或删除元素的操作,效率较高
在这三个
List
实现类中,使用占比为:ArrayList(95%)、Vector(4%)、LinkedList(1%)
尽管ArrayList
是最常用的,但我们应该根据业务场景作出选择
在各种list中,在不要求同步的情况下,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList; Vector总是比ArrayList慢,所以尽量避免使用
Set接口
Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。
Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义的 get(int index)方法,所以无法使用循环进行输出。
该接口中有两个常用的子类:HashSet、TreeSet。
HashSet
HashSet 属于散列的存放类集,里面的内容是无序存放的。
public class HashSetDemo { public static void main(String[] args) { Set<String> all = new HashSet<String>(); // 实例化Set接口对象 all.add("A"); all.add("A");//重复元素 all.add("A");//重复元素 all.add("B"); all.add("P"); all.add("D"); all.add("E"); System.out.println(all); //将其转换为Object数组,不推荐,因为在操作的时候已经指定了操作的泛型类型 Object[] objects = all.toArray(); //将其转换为指定的泛型类型数组 String[] array = all.toArray(new String[]{}); for (int i = 0; i < array.length; i++) { System.out.print(array[i] + ", "); } } }
控制台输出如下:
以上字符串“A”设置了很多次,因为 Set 接口中是不能有任何的重复元素的,所以其最终结果只能有一个“A”。
TreeSet
- 与 HashSet 不同的是,TreeSet 属于可以子类。
- 存储基础数据类型示例:
public class TreeSetDemo { public static void main(String[] args) { Set<String> all = new TreeSet<>(); // 实例化Set接口对象 all.add("P"); all.add("E"); all.add("D"); System.out.println(all); } }
控制台输出:
可以看到,TreeSet
集合对基本数据类型的数据自动进行了排序
- 如果存储自定义类类型,该自定义类必须实现
Comparable
接口,如果未实现该接口,则会抛出ClassCastException
异常,示例:
Person类:
public class Person implements Comparable<Person> { private String name; private int age; public int compareTo(Person per) { if (this.age > per.age) { return 1; } else if (this.age < per.age) { return -1; } else { return 0; } } public Person() { } public Person(String name, int age) { 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 toString() { return "姓名:" + this.name + ",年龄:" + this.age; } }
TreeSetDemo类
public class TreeSetDemo { public static void main(String[] args) { Set<Person> all = new TreeSet<>(); // 实例化Set接口对象 all.add(new Person("张三", 10)); all.add(new Person("李四", 12)); all.add(new Person("王五", 14)); all.add(new Person("赵六", 10)); all.add(new Person("孙七", 13)); System.out.println(all); } }
控制台输出如下:
从以上的结果可以看到我们已经实现了年龄排序,但是我们发现赵六没有了。因为赵六的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则此时必须修改 Person 类,如果假设年龄相等的话,按名字进行排序。
修改的compareTo方法如下:
public int compareTo(Person per) { if (this.age > per.age) { return 1; } else if (this.age < per.age) { return -1; } else { return this.name.compareTo(per.name); } }
- 我们也可以通过TreeSet()构造方法传入一个
Comparator
的实现类来实现自定义类型比较,示例:
public class TreeSetDemo { public static void main(String[] args) { Set<Person> all = new TreeSet<>(new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { if (p1.getAge() > p2.getAge()) { return 1; } else if (p1.getAge() < p2.getAge()) { return -1; } else { return p1.getName().compareTo(p2.getName()); } } }); // 实例化Set接口对象,传入Comparator对象 all.add(new Person("张三", 10)); all.add(new Person("李四", 12)); all.add(new Person("王五", 14)); all.add(new Person("赵六", 10)); all.add(new Person("孙七", 13)); System.out.println(all); } }
控制台输出如下:
可以看出两者得到的结果相同,但这种传入Comparator
接口只能使用一次,而且每次都需要传入Comparator对象。
关于重复元素的说明
之前使用 Comparable 完成的对于重复元素的判断,那么 Set 接口定义的时候本身就是不允许重复元素的,那么证明
如果现在真的是有重复元素的话,使用 HashSet 也同样可以进行区分。
public class HashSetPersonDemo { public static void main(String[] args) { Set<Person> all = new HashSet<Person>(); all.add(new Person("张三", 10)); all.add(new Person("李四", 10)); all.add(new Person("李四", 10)); all.add(new Person("王五", 11)); all.add(new Person("赵六", 12)); all.add(new Person("孙七", 13)); System.out.println(all); } }
此时我们发现姓名:李四,年龄:10
的对象出现了两次,并没有去掉所谓的重复元素,也就是说之前的操作并不是真正的重复元素的判断,而是通过 Comparable 接口间接完成的。
因此,如果我们要想判断两个对象是否相等,则必须使用 Object 类中的 equals()方法。
从最正规的来讲,如果要想判断两个对象是否相等,则有两种方法可以完成:
- 第一种判断两个对象的编码是否一致,这个方法需要通过 hashCode()完成,即:每个对象有唯一的编码
- 还需要进一步验证对象中的每个属性是否相等,需要通过 equals()完成。
所以此时需要覆写 Object 类中的 hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的。
public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Person)) { return false; } Person per = (Person) obj; if (per.name.equals(this.name) && per.age == this.age) { return true; } else { return false; } } public int hashCode() { return this.name.hashCode() * this.age; }
我们发现,此时已经不存在重复元素了,所以如果要想去掉重复元素是需要依靠hashCode()和 equals()方法共同完成的。
这篇关于Java学习笔记之集合(上)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-25Java语音识别项目入门:新手必读指南
- 2024-11-25Java语音识别项目入门:轻松开始你的第一个语音识别项目
- 2024-11-25Java语音识别项目入门详解
- 2024-11-25Java语音识别项目教程:从零开始的详细指南
- 2024-11-25JAVA语音识别项目教程:初学者指南
- 2024-11-25Java语音识别项目教程:初学者指南
- 2024-11-25JAVA云原生入门:新手指南与基础教程
- 2024-11-25Java云原生入门:从零开始的全面指南
- 2024-11-25Java云原生入门:新手必读教程
- 2024-11-25JAVA云原生教程:新手入门及实战指南