java集合----collection接口
引入
面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。 而Java 集合就像一种容器,可以动态地 把多个对象的引用放入容器中。另外一方面,使用 Array 存储对象方面具有 一些弊端 。
比如:
-
数组初始化以后,长度就不可变了,不便于扩展;
-
数组中提供的属性和方法少,不便于进行添加、删除、插入等操作, 且效率 不 高。
同时无法直接获取存储元素的个数; -
数组存储的数据是有序的、可以重复的。 存储数据的特点单一;
在java中集合框架主要分为Collection和Map两种体系.
这里主要讲解有关于Collection的一些操作。
Collection接口
Collection 接口是 List 、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
这里可以看看在Java8中关于Collection接口的介绍:
翻译为:集合层次结构中的根接口。集合表示一组对象,称为其元素。有些集合允许复制元素,有些则不允许。有些是有序的,有些是无序的。JDK没有提供该接口的任何直接实现:它提供了更具体的子接口(如Set和List)的实现。此接口通常用于在需要最大通用性的地方传递和操作集合。
Collection接口的方法
- add(T e): 添加一个元素(这里T表示泛型的意思)
例子:
这里代码只给出了测试单元,可以看见Colletion接口add方法既可以添加int型,String型,还可以是个对象(这里Person是我创建的一个对象)
public void test1() {
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",20));
Iterator iterator = obj.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
结果:
- addAll(Collection c ): 将指定的对象的集合加到当前集合中
例如:
@Test
public void test1() {
Collection obj = new ArrayList();
//将数组元素转化为list列表
Collection obj2 = Arrays.asList("123",132);
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",20));
obj.addAll(obj2);
Iterator iterator = obj.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
结果:
- int size() : 获取有效元素的个数
例如:这里使用size方法去返回obj中的元素个数
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
System.out.println(obj.size());
}
结果:
-void clear():清空集合
例如:使用clear方法后,我们再看看obj的size是多少
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.clear();
System.out.println(obj.size());
}
结果,毋庸置疑的是0:
- boolean isEmpty(): 判断是否是空集合
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
boolean empty = obj.isEmpty();
System.out.println(empty);
}
结果:
- boolean contains(Object obj): 是通过元素的 equals 方法来判断是否是同一个对象
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
boolean contains = obj.contains(123);
System.out.println(contains);
}
结果:
- boolean containsAll(Collection c) 也是调用元素的 equals 方法来比较的 。拿两个集合的元素挨个比较 。
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
Collection obj2 = Arrays.asList(123);
obj.add(123);
obj.add("AA");
boolean containsAll = obj.containsAll(obj2);
System.out.println(containsAll);
}
结果:
- boolean remove(Object obj) 通过 元素的 equals 方法判断是否是要删除的那个元素 。 只会删除找到的第一个元素。
例如:
@Test
public void test3(){
Collection obj = new ArrayList();
obj.add(123);
obj.add(123);
obj.add("AA");
obj.add("BB");
obj.remove(123);
Iterator iterator = obj.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
结果:
可见并没有把相同的元素全部删去,只删除第一个相同的元素。
- boolean removeAll(Collection coll 取当前集合的差集
例如:
@Test
public void test3(){
Collection obj = new ArrayList();
Collection obj2 = Arrays.asList(123,"AA");
obj.add(123);
obj.add(123);
obj.add("AA");
obj.add("BB");
obj.removeAll(obj2);
Iterator iterator = obj.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
结果:
- boolean equals(Object obj) :集合是否相等
- 这里我们来看一个例子,我用一个Person类的一个对象,加到obj中,obj2用Arrays的方法把数组转化为List,其中元素也是Person类的一个对象。
person类如下所示:
package com.Collection;
public class Person {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// @Override
// public boolean equals(Object o) {
// System.out.println("Person equals()....");
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
//
// Person person = (Person) o;
//
// if (age != person.age) return false;
// return name != null ? name.equals(person.name) : person.name == null;
// }
//
// @Override
// public int hashCode() {
// int result = name != null ? name.hashCode() : 0;
// result = 31 * result + age;
// return result;
// }
}
测试单元如下:
@Test
public void test5(){
Collection obj = new ArrayList();
obj.add(new Person("LC",12));
Collection obj2 = Arrays.asList(new Person("LC",12));
boolean equals = obj.equals(obj2);
System.out.println(equals);
}
看看结果(Person类中的注释没打开):
现在我在Person类打开了注释,再看看结果:
解释:第一次判断结果为false,是因为在执行equals方法的时候,调用的是Object里面的equals方法
因为地址值不同,所以返回false.
而第二种是自己重写了equals方法,没有按照地址值判断,所以返回true
- Object[] toArray() 转成对象数组
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
Object[] objects = obj.toArray();
System.out.println(objects[1]);
}
结果:
- hashCode(): 获取集合对象的哈希值
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
System.out.println(obj.hashCode());
}
结果:
- iterator() 返回迭代器对象,用于集合遍历
其实在试前面的方法的时候已经用到了。
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
Iterator iterator = obj.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
结果:
注意: Collection obj = new Collection(); //错误,因为接口不能实例化
Collection接口虽然不能直接被使用, 但提供了集合以及集合元素的方法且Set接口和List接口都可以调用Collection接口的方法。
Collection的子接口(一)—List接口
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组。
通过源代码可以发现List接口继承与Collecion接口
List特点:
- List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK中List接口的实现类常用的有:ArrayList、LinkedList和Vector
List类的一些方法
List 除了从 Collection 集合继承的方法外, List 集合里添加了一些根据索引来操作集合元素的方法,下面就来了解一下它的一些方法.
这里我用ArrayList来实现List接口中的一些方法
- void add( int index, Object o) 在 index 位置插入o元素.
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
System.out.println(list);
list.add(1,"RR");
System.out.println(list);
}
结果:
- boolean addAll(int index, Collection eles) 从 index 位置开始将 eles 中的所有元素添加进来
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
list.addAll(1, Arrays.asList("123","456"));
System.out.println(list);
}
结果:
- Object get( int index): 获取指定 index 位置的元素
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
Object o = list.get(1);
System.out.println(o);
}
结果:
- int indexOf(Object obj):返回obj在集合中首次出现的位置,没找到返回-1
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("BB");
list.add("BB");
int num1 = list.indexOf("BB");
int num2 = list.indexOf("Baa");
System.out.println(num1+" "+num2);
}
结果:
- int lastlndexOf(Object obj):返回obj在当前集合中末次出现的位置,没找到返回-1
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("BB");
list.add("BB");
int num1 = list.lastIndexOf("BB");
int num2 = list.lastIndexOf("Baa");
System.out.println(num1+" "+num2);
}
结果:
- Object remove(int index):移除指定index位置的元素,并返回此元素
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
Object o = list.remove(1);
System.out.println(o);
}
结果:
- Object set(int index,Object ele):设置指定index位置的元素为ele,并返回之前在index位置的值
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
Object o = list.set(1, "CC");
System.out.println(o);
System.out.println(list);
}
结果:
- List subList(int fromlndex,int tolndex):返回从fromlndex到tolndex位置的子集合
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
List list1 = list.subList(1, 3);
System.out.println(list1);
}
结果:
List实现类之一----ArrayList
看ArrayList的源码发现,ArrayList继承于AbstractList, 实现了List、RandomAccess, Cloneable, java.io.Serializable。
ArrayList 是 List 接口的典型 实现类、主要实现类。本质上, ArrayList 是对象引用的 一个变长数组
ArrayList的构造方法
从构造方法中我们可以看见,默认情况下,elementData是一个大小为0的空数组,当我们指定了初始大小的时候,elementData的初始大小就变成了我们所指定的初始大小了。
List实现类之二—LinkedList
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
prev变量: 记录前一个元素的位置
next变量: 记录下一个元素的位置
对于 频繁的插入或删除元素 的操作,建议 使用 LinkedList 类,效率 较高
这是它的源码:
LinkedList的一些方法
除了List中的一些方法,LinkedList还有一些方法。
- void addFirst(Object obj)
- void addLast(Object obj)
- Object getFirst()
- Object getLast()
- Object removeFirst()
- Object removeLast()
就看一个例子,来了解下这些方法:
@Test
public void test3(){
LinkedList linkedList = new LinkedList();
linkedList.add("AA");
linkedList.add("BB");
linkedList.add("CC");
linkedList.add("DD");
linkedList.add("EE");
linkedList.add("FF");
linkedList.addFirst("begin");
linkedList.addLast("last");
System.out.println(linkedList);
System.out.println("***************");
Object first = linkedList.getFirst();
Object last = linkedList.getLast();
System.out.println(first);
System.out.println(last);
Object o = linkedList.removeFirst();
Object o1 = linkedList.removeLast();
System.out.println(linkedList);
}
结果:
Vector
Vector是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
Vector的一些方法
-
void addElement (Object obj
-
void insertElementAt (Object obj,int index)
-
void setElementAt (Object obj,int index)
-
void removeElement (Object obj
-
void removeAllElements
看一个例子:
public void test4(){
Vector vector = new Vector();
vector.addElement("aaa");
vector.addElement("aaa");
vector.addElement("bbb");
vector.insertElementAt("ccc",1);
System.out.println(vector);
vector.setElementAt("ddd",1);
System.out.println(vector);
vector.removeElement("aaa");
System.out.println(vector);
vector.removeAllElements();
System.out.println("现在还剩下元素"+vector);
}
结果:
一道关于ArrayList/LinkedList/Vector的面试题
问题:请问ArrayList/LinkedList/Vector 的 异同 谈谈你的理解? ArrayList 底层
是什么?扩容机制? Vector 和 ArrayList 的最大区别。
ArrayList 和 LinkedList 的 异同二者都线程不安全,相对线程安全的Vector ,执行效率高。
此外,ArrayList 是实现了基于动态数组的数据结构, LinkedList 基于链表的数据结构。对于
随机访问 get 和 set ArrayList 觉得优于 LinkedList ,因为 LinkedList 要移动指针。对于新增
和删除 操作 add( 特指 插入 和 remove LinkedList 比较占优势,因为 ArrayList 要移动数据。
ArrayList 和 Vector 的区别Vector和 ArrayList 几乎是完全相同的 唯一的区别在于 Vector 是同步类 ( synchronized),属于强同步类。因此开销就比 ArrayList 要大,访问要慢。正常情况下 大多数的 Java 程序员使用ArrayList 而不是 Vector, 因为同步完全可以由程序员自己来控制。 Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。 Vector 还有一个子 类 Stack
Collection的子接口(二)—set接口
概述
-
Set 接口 是 Collection 的子接口, set 接口没有 提供额外 的方法
-
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个
-
Set 集合中,则添加操作失败。
-
Set 判断两个对象是否相同不是使用 == 运算符,而是 根据 equals() 方法
Set实现类之一— Hashset
概述
- HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
- HashSet 按 Hash 算法来存储集合中的元素,因此具有很好 的 存取、查找、删除性能。
- HashSet具有以下特点:
不能保证元素的排列顺序
HashSet不是线程安全的
集合元素可以是null - HashSet 集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
- 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
Set的无序性和不可重复型:
例子(这里的Person类是我自己创建的):
@Test
public void test(){
HashSet set = new HashSet();
set.add("AA");
set.add("BB");
set.add("CC");
set.add("CC");
set.add(new Person("LC",20));
set.add(new Person("LC",20));
System.out.println(set);
}
猜猜结果是多少?
发现这里把两个Person类的对象都输出来了
如果在重写equals()和hashcode()方法,那会怎么样呢
@Override
public boolean equals(Object o) {
System.out.println("Person equals()....");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
此时输出就只有一个Person对象了
向 HashSet 中添加元素的过程:
-
当向 HashSet 集合中存入一个元素时 ,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值 ,通过某种散列函数决定该对象在 HashSet 底层数组中 的 存储位置 。 这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布该散列函数设计的越好(这就和数组结构有点关系了)
-
如果两个元素的 hashCode() 值相等,会再继续调用 equals 方法。如果 equals 方法结果为 true 添加 失败。如果 为 false 那么会保存该元素 但是该数组的位置已经有元素了那么会通过链表的方式,继续链接 。
-
如果 两个元素的 equals() 方法返回 true ,但它们的 hashCode () 返回值不相等, hashSet 将会把它们存储在不同的位置,但依然可以添加 成功。
Set实现类之二—LinkedHashset
概述
- LinkedHashSet 是HashSet的子类
- LinkedHashSet 根据元素的 hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
- LinkedHashSet不允许集合元素重复。
看一个关于LinkedHashset的例子
@Test
public void test(){
LinkedHashSet set = new LinkedHashSet();
set.add("AA");
set.add("BB");
set.add("CC");
set.add(new Person("LC",20));
set.add("a");
set.add("b");
System.out.println(set);
}
输出结果:
可以发现它的输出结果顺序就是添加的顺序。
在Linkedhashset的底层结构
Set实现类之三—Treeset
概要:
-
TreeSet 是 SortedSet 接口的实现类, TreeSet 可以确保集合元素处于排序 状态。
-
TreeSet 底层使用 红黑树 结构存储数据
-
TreeSet 两种排序方法: 自然排序 和 定制排序 。默认情况下, TreeSet 采用自然排序。
自然排序:TreeSet 会调用集合元素的compare To(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
定制排序:通过 Comparator 接口来实现。 需要重写 compare(T o1,T o2) 方法
例子:
@Test
public void test2(){
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(3);
treeSet.add(5);
treeSet.add(2);
treeSet.add(4);
treeSet.add(6);
Object first = treeSet.first();
Object last = treeSet.last();
Object o = treeSet.higher(5);
System.out.println(treeSet);
System.out.println(first);
System.out.println(last);
System.out.println(o);
}
结果:
如果有错误,请指出 😄
喜欢的话,就点个赞吧!!!