文章目录
概述
集合是Java用于处理数据的工具。集合中的数据存储在内存,不具有持久性。
Java中的集合分为 单列集合 与 双列集合。单列集合 存储纯粹的目标对象,对应Collection
接口;双列集合 存储key-value
(键值对)数据,对应Map
接口。
单列集合分为 有序集合 与 无序集合。有序集合的典型为List
接口的实现;无序集合的典型为Set
接口的实现。Collection
是泛型接口。
Collection
中的常用方法
public interface Collection<E> extends Iterable<E> {
/* 返回集合中元素个数 */
int size();
/* 如果集合中没有元素,返回true */
boolean isEmpty();
/* 如果集合中至少包含1个与指定对象相等的元素,返回true.
注意contains()并非判断是否包含同一个对象,而是通过
调用传入对象的equals()判断是否包含相等元素。
因此目标对象类必须重写equals()。 */
boolean contains(Object o);
/* 返回集合的遍历。已返回遍历中的元素顺序不能保证,除非集合实例是有序的 */
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(t[] a);
/* 向集合添加一个指定元素。如果集合由于本次调用添加而改变,返回true;
如果集合不支持添加重复元素且已包含调用指定的元素,或者集合拒绝
添加某些元素(例如有的集合拒绝添加null),返回false. */
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean add(E e);
/* 从集合移除一个指定元素,若有多个元素与指定对象相等,仅仅移除第一个。
如果集合由于本次调用移除而改变(即集合中存在一个或多个与指定对象相等的元素),返回true. */
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean remove(Object o);
/* 如果集合中包含指定集合中的所有元素,返回true. */
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean containsAll(Collection<?> c);
/* 将指定集合中的所有元素添加至集合。如果集合由于本次调用添加而改变,返回true. */
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean addAll(Collection<? extends E> c);
/* 移除集合中所有与指定集合的交集元素,包括重复元素。
如果集合由于本次调用移除而改变(集合与目标集合有交集),返回true. */
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean removeAll(Collection<?> c);
/* 保留集合中所有与指定集合的交集元素(或删除集合中与指定集合的差集元素),包括重复元素。
如果集合由于本次调用移除而改变(集合与目标集合有差集),返回true.*/
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean retainAll(Collection<?> c);
/* 清除集合中的所有元素 */
void clear();
/* 比较集合是否与指定集合相等。两个集合的 元素个数形同、对应元素相等(List集合的有序性),
则两个几何相等。*/
/* 由于需要判断是否包含指定元素,目标对象类必须重写equals(). */
boolean equals(Object o);
int hashCode();
}
扩展
Arrays.asList()
asList()
是Arrays
类中的静态方法:
static <T> List<T> asList(T... a)
将数组转换为List
集合。例:
String[] strs = new String[]{"Tony","Tom","Jerry"};
Integer[] ints = new Integer[]{111,222,333};
List<?> list_1 = Arrays.asList(strs);
List<?> list_2 = Arrays.asList(ints);
List<?> list_3 = Arrays.asList("Tony","Tom","Jerry");
List<?> list_4 = Arrays.asList(111,222,333);
注意:将数组int[]{...}
转换为List
时会出现预期之外的情况,asList()
会将int[]{...}
作为一个数组元素。float
、double
等类似,应当使用包装类代替。例:
@Test
public void test(){
List<?> list = Arrays.asList(new int[]{111,222,333});
System.out.println(list.size());
System.out.println(list);
}
运行结果:
1
[[I@10f87f48]
使用包装类代替:
@Test
public void test(){
List<?> list = Arrays.asList(new Integer[]{111,222,333});
System.out.println(list.size());
System.out.println(list);
}
运行结果:
3
[111, 222, 333]
Iterator
概述:Iterator
是泛型接口,用于集合的遍历。Iterator
构造出虚拟指针用于得到集合中的下一个元素。注意:Iterator
并不会创造一个新的集合容器以实现集合的遍历,它只是可以读取或改变集合内容的迭代器。
E next()
:泛型方法,返回集合中的下一个元素。
注意:
①指针初始位于集合中第一个元素之前的空位,第一次调用next()
将返回集合的第一个元素。
②每次调用next()
都会导致指针向前移动。指针已经处于集合末尾时,调用next()
会抛NoSuchElementException
。
boolean hasNext()
:判断是否有下一个元素。如果指针已经位于集合的末尾,hasNext()
将返回false
。
**注意:**调用hasNext()
不会 导致指针移动。
ArrayList<String> list = new ArrayList<>();
list.add("Tony");
list.add("Tom");
list.add("Jerry");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+" ");
}
void remove()
:移除集合中当前指针所在位置的元素。
注意:
①第一次调用next()
之前不可使用remove()
,否则会抛IllegalStateException
。因为指针初始位于集合第一个元素之前的空位。
②不可在同一位置调用超过一次remove()
,否则会抛IllegalStateException
。
ArrayList<String> list = new ArrayList<>();
list.add("Tony");
list.add("Tom");
list.add("Jerry");
list.add("Julia");
list.add("Marry");
list.add("Mophy");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String s = iterator.next();
System.out.print(s+" ");
if("Jerry".equals(s)){
iterator.remove();
}
}
System.out.print("\r\n");
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
System.out.print(iter.next()+" ");
}
List
概述:List
集合的特性是 有序性 可重复性。List
接口的常用实现有ArrayList<>
、LinkedList
和Vector
。
List
中的常用方法:
/* 将新的元素添加至List的index位置,原本index位置及其以后的元素都将后移。 */
void add(int index,E element)
/* 将新的集合添加至List的index位置,新的集合的第一个元素将占据List中原本index位置,
List中原本index位置及其以后的元素都将后移。*/
/* 如果集合由于本次调用添加而改变,返回true. */
boolean addAll(int index,Collection<? extends E> c)
/* 返回List中 第一个 与指定对象相等的元素索引。
如果List中不包含与指定对象相等的元素,返回 -1. */
/* 由于需要判断元素是否与指定对象相等,目标对象类必须重写equals(). */
int indexOf(Object o)
/* 返回List中 最后一个 与指定对象相等的元素索引。
如果List中不包含与指定对象相等的元素,返回 -1. */
/* 由于需要判断元素是否与指定对象相等,目标对象类必须重写equals(). */
int lastIndexOf(Object o)
/* 获得List中指定索引位置的元素。
如果指定索引超出范围,抛 IndexOutOfBoundsException. */
E get(int index)
/* 设置List中指定索引位置的元素的值,并返回该位置元素的 原本值。 */
E set(int index,E element)
/* 移除List中指定索引位置的元素,并返回该位置 移除之前 元素的值。 */
E remove(int index)
/* 获得List中指定索引区间 [fromIndex,toIndex) 的子List集合.
包含fromIndex,不包含toIndex.*/
List<E> subList(int formIndex,int toIndex)
/* 返回指定集合的复制,该复制不可进行更改,否则会抛异常。
本体的后续修改不会影响该复制。*/
static <E> List<E> copyOf(Collection<? extends E> coll)
/* 对List集合进行定制排序 */
default void sort(Comparator<? super E> c)
注意:使用remove(Object o)
和remove(int index)
时注意区分:对于int
型的List
而言,使用remove((int)n)
时,系统会认为调用的是remove(int index)
而不是remove(Object o)
.如果需要从int
型List
中移除指定元素,可以采取封装类:remove(Integer.valueOf(n))
.例如:
@Test
public void test(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.remove(Integer.valueOf(2)); // public static Integer valueOf(int i)
System.out.println(list); //输出[1,3]
}
定制排序:
@Test
public void test(){
LinkedList<String> list = new LinkedList<>();
list.add("Tony");
list.add("Jerry");
list.add("Lucy");
list.add("Steve");
list.add("Tom");
list.sort(new Comparator<String>(){
@Override
public int compare(String st1,String st2){
//从小到大排序
return st1.compareTo(st2);
}
});
Iterator iterator_1 = list.iterator();
while(iterator_1.hasNext()){
System.out.print(iterator_1.next()+" ");
}
System.out.println();
list.sort(new Comparator<String>(){
@Override
public int compare(String st1,String st2){
//从大到小排序
return -(st1.compareTo(st2));
}
});
Iterator iterator_2 = list.iterator();
while(iterator_2.hasNext()){
System.out.print(iterator_2.next()+" ");
}
}
运行结果:
Jerry Lucy Steve Tom Tony
Tony Tom Steve Lucy Jerry
ArrayList
概述:ArrayList
底层由Object[]
实现。线程不安全,执行效率高。
ArrayList
中常用的新方法:
/* 返回ArrayList实例的 浅拷贝,元素本身不会被复制。*/
Object clone()
@Test
public void test(){
ArrayList<String> list = new ArrayList<>();
list.add("Tony");
list.add("Jerry");
list.add("Mophy");
ArrayList<String> cloneList = (ArrayList<String>)list.clone();
cloneList.remove(1);
System.out.println(list); //["Tony","Jerry","Mophy"]
System.out.println(cloneList); //["Tony","Mophy"]
}
注意:浅拷贝是数值层面的拷贝,本体和副本指向不同对象;深拷贝是对象层面的拷贝,本体和副本指向相同对象。
/* 将ArrayList实例的容量调整为当前列表的大小。
通过此操作可最小化ArrayList实例的存储,以优化应用程序。 */
void trimToSize()
LinkedList
概述:LinkedList
底层由双向链表实现。用于需要频繁插入、删除数据的场合。执行效率比ArrayList
高。
LinkedList
中常用的新方法:
/* 在List的开头插入指定元素 */
void addFirst(E e)
/* 在List的末尾追加指定元素,等同于 add() */
void addLast(E e)
/* 获得List的第一个元素 */
E getFirst()
/* 获得List的最后一个元素 */
E getLast()
@Test
public void test(){
LinkedList<String> list = new LinkedList<>();
list.add("Tony");
list.add("Jerry");
list.add("Mike");
list.add("Tony");
list.add("Sandy");
System.out.println(list.size());
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){System.out.print(iterator.next()+" ");}
}
运行结果:
5
Tony Jerry Mike Tony Sandy
Vector
概述:Vector
底层由Object[]
实现。线程安全,执行效率低。
@Test
public void test(){
Vector<String> v = new Vector<>();
v.add("Tony");
v.add("Jerry");
v.add("Mike");
v.add("Tony");
v.add("Sandy");
System.out.println(v.size());
Iterator<String> iterator = v.iterator();
while(iterator.hasNext()){System.out.print(iterator.next()+" ");}
}
运行结果:
5
Tony Jerry Mike Tony Sandy
Set
概述:Set
的特性是:无序性、不重复性。
无序性:存储的数据在底层数组中并非按照数组的索引顺序添加,而是由数据的hashCode
决定。
不重复性:Set
集合中的数局在数值层面没有重复元素。向Set
集合中添加已经存在的数据不会改变Set
集合。
Set
集合添加数据的过程
Set
集合根据数据的哈希值使用某种算法确定数据的存放位置。Set
存放数据的步骤为:
①.调用将要添加数据对象的hashCode()
方法确定存放位置,该位置为空则添加成功。该位置不为空则进行第②步。
②.比较该位置已存在数据和将要添加数据的哈希值,不相同则添加成功,将新数据在该位置与原本的数据以链表形式保存(已存在的数据指向新添加的数据)。相同则进行第③步。
③.调用将要添加数据对象的equals()
方法比较是否与该位置已存在数据的值相等。不相等则添加成功,相等则添加失败。
HashSet
概述:HashSet
集合遍历结果中的元素顺序与添加顺序不同,遍历顺序由元素的哈希值决定。HashSet
是线程不安全的。
@Test
public void test(){
HashSet<String> set = new HashSet<>();
set.add("Tony");
set.add("Jerry");
set.add("Mike");
//重复数据将添加失败
set.add("Tony");
set.add("Sandy");
System.out.println(set.size());
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){System.out.print(iterator.next()+" ");}
}
运行结果:
4
Tony Mike Sandy Jerry
LinkedHashSet
概述:LinkedHashSet
是HashSet
的子类。LinkedHashSet
在HashSet
的基础上在元素之间按照添加顺序增加了双向链表地址链接,因此LinkedHashSet
集合遍历结果的元素顺序与添加顺序一致。对于频繁的遍历操作,LinkedHashSet
效率高于HashSet
。
@Test
public void test(){
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("Tony");
set.add("Jerry");
set.add("Mike");
//重复数据将添加失败
set.add("Tony");
set.add("Sandy");
System.out.println(set.size());
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){System.out.print(iterator.next()+" ");}
}
运行结果:
4
Tony Jerry Mike Sandy
TreeSet
概述
TreeSet
的底层是 红黑树 结构,可对集合中的元素进行自然排序和定制排序。
向TreeSet
集合中添加的数据必须是可比较的,必须为该数据类型制定比较规则(Comparable
或Comparator
)。
与HashSet
和LinkedHashSet
不同,TreeSet
不使用equals()
方法判断两个数据是否相等,而是通过为数据类型制定的比较规则判断。compareTo()
或compare()
返回0
则认为两个数据相等,数据添加失败;compareTo()
或compare()
返回1
或-1
则认为两个数据不相等,数据添加成功。
TreeSet
的自然比较
Person.java
public class Person implements Comparable<Person> {
private String name;
private float height;
private int weight;
public Person(String name,float height,int weight){
this.name = name;
this.height = height;
this.weight = weight;
}
public void setName(String name){this.name = name;}
public String getName(){return this.name;}
public void setHeight(float height){this.height = height;}
public float getHeight(){return this.height;}
public void setWeight(int weight){this.weight = weight;}
public int getWeight(){return this.weight;}
@Override
public int compareTo(Person p){
//name相同直接认为数据相等
if(this.getName().equals(p.getName())) return 0;
else {
//一级比较:height
if(this.getHeight() > p.getHeight()) return 1;
else if(this.getHeight() < p.getHeight()) return -1;
else {
//二级比较:weight
if(this.getWeight() > p.getWeight()) return 1;
else if(this.getWeight() < p.getWeight()) return -1;
//三级比较:name
return this.getName().compareTo(p.getName());
}
}
}
}
Main.java
public class Main {
public static void main(String[] args){
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("Tony",1.75f,130));
set.add(new Person("Steve",1.80f,140));
set.add(new Person("Jerry",1.75f,120));
//new Person("Tony",1.70f,125)不会成功添加,因为比较规则认为name相同,数据相同
set.add(new Person("Tony",1.70f,125));
set.add(new Person("Mike",1.72f,130));
Iterator<Person> iterator = set.iterator();
while(iterator.hasNext()){
Person p = iterator.next();
System.out.println(p.getName()+" "
+p.getHeight()+"m "
+p.getWeight()+"(1/2Kg).");
}
}
}
运行结果:
Mike 1.72m 130(1/2Kg).
Jerry 1.75m 120(1/2Kg).
Tony 1.75m 130(1/2Kg).
Steve 1.8m 140(1/2Kg).
TreeSet
的定制比较
Person.java
public class Person {
private String name;
private float height;
private int weight;
public Person(String name,float height,int weight){
this.name = name;
this.height = height;
this.weight = weight;
}
public void setName(String name){this.name = name;}
public String getName(){return this.name;}
public void setHeight(float height){this.height = height;}
public float getHeight(){return this.height;}
public void setWeight(int weight){this.weight = weight;}
public int getWeight(){return this.weight;}
}
Main.java
public class Main {
public static void main(String[] args){
//set_1元素从小到大排序
TreeSet<Person> set_1 = new TreeSet<>(new Comparator<Person>(){
@Override
public int compare(Person p1,Person p2){
//name相同直接认为数据相等
if(p1.getName().equals(p2.getName())) return 0;
else {
//一级比较:height
if(p1.getHeight() > p2.getHeight()) return 1;
else if(p1.getHeight() < p2.getHeight()) return -1;
else {
//二级比较:weight
if(p1.getWeight() > p2.getWeight()) return 1;
else if(p1.getWeight() < p2.getWeight()) return -1;
//三级比较:name
return p1.getName().compareTo(p2.getName());
}
}
}
});
set_1.add(new Person("Tony",1.75f,130));
set_1.add(new Person("Steve",1.80f,140));
set_1.add(new Person("Jerry",1.75f,120));
//new Person("Tony",1.70f,125)不会成功添加,因为比较规则认为name相同,数据相同
set_1.add(new Person("Tony",1.70f,125));
set_1.add(new Person("Mike",1.72f,130));
//set_2元素从大到小排序
TreeSet<Person> set_2 = new TreeSet<>(new Comparator<Person>(){
@Override
public int compare(Person p1,Person p2){
//name相同直接认为数据相等
if(p1.getName().equals(p2.getName())) return 0;
else {
//一级比较:height
if(p1.getHeight() > p2.getHeight()) return -1;
else if(p1.getHeight() < p2.getHeight()) return 1;
else {
//二级比较:weight
if(p1.getWeight() > p2.getWeight()) return -1;
else if(p1.getWeight() < p2.getWeight()) return 1;
//三级比较:name
return -(p1.getName().compareTo(p2.getName()));
}
}
}
});
set_2.add(new Person("Tony",1.75f,130));
set_2.add(new Person("Steve",1.80f,140));
set_2.add(new Person("Jerry",1.75f,120));
//new Person("Tony",1.70f,125)不会成功添加,因为比较规则认为name相同,数据相同
set_2.add(new Person("Tony",1.70f,125));
set_2.add(new Person("Mike",1.72f,130));
Iterator<Person> iterator_1 = set_1.iterator();
while(iterator_1.hasNext()){
Person p = iterator_1.next();
System.out.println(p.getName()+" "
+p.getHeight()+"m "
+p.getWeight()+"(1/2Kg).");
}
System.out.println();
Iterator<Person> iterator_2 = set_2.iterator();
while(iterator_2.hasNext()){
Person p = iterator_2.next();
System.out.println(p.getName()+" "
+p.getHeight()+"m "
+p.getWeight()+"(1/2Kg).");
}
}
}
运行结果:
Mike 1.72m 130(1/2Kg).
Jerry 1.75m 120(1/2Kg).
Tony 1.75m 130(1/2Kg).
Steve 1.8m 140(1/2Kg).
Steve 1.8m 140(1/2Kg).
Tony 1.75m 130(1/2Kg).
Jerry 1.75m 120(1/2Kg).
Mike 1.72m 130(1/2Kg).