简介
说明
本文介绍Java中List的使用,包括:创建、遍历、去重、排序、删除。
创建
法1:构造 List 后使用 List.add(繁琐)
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
法2:Arrays.asList
List<String> list = Arrays.asList("a", "b");
或者
List<String> list = new ArrayList<>(Arrays.asList("a", "b"));
法3:Stream (JDK8)
List<String> list = Stream.of("a", "b").collect(Collectors.toList());
法4:{{}}
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
说明
外层的 {} 定义了一个 ArrayList 的匿名内部类。内层的 {} 的定义了一个实例初始化代码块。 这个代码块在初始化内部类时执行。所以这里相当于定义了一个匿名内部类,并使用 add 添加元素来初始化。
{{}} 双括号语法同样可用于初始化 Map 等其他众多类型。
缺点
- 使用匿名内部类,会有效率上的损失。当然在大多数情况下,这点效率都是可接受的。
- 非静态内部类持有所在外部类的引用,可能造成内存泄漏。
法5:google的guava(JDK9)
List<String> list = Stream.of("a", "b").collect(Collectors.toList());
遍历
简介
说明
- 记录的存取方式有两种:一种是数组方式存储,另一种是链表方式。
- 对于数组方式存储的记录可以根据其下标找到对应的记录,而链表方式存储(单链表为例)则必须找到其前一个记录的位置才能够找到本记录。
- 所以:for循环便于访问数组方式存储的记录,比如数组等;而迭代则更适用于链表存储的记录。
法1:for循环
List<Object> list = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
Object value = list.get(i);
}
法2:增强for循环
List<Object> list = new ArrayList<>();
for (Object s : list) {
Object value = s;
}
法3:foreach
List<Object> list = new ArrayList<>();
list.forEach(o -> {Object value = o});
实际上,foreach底层也是通过Iterator来遍历,上边的代码反编译之后:
List<Object> list = new ArrayList<>();
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Object value = iterator.next();
}
法4:迭代器(Iterator)
Iterator<Object> iter = list.iterator();
while (iter.hasNext()) {
Object value = iter.next();
}
性能
结论
ArrayList:按性能由高到低排序:forEach > Iterator > 增强for > for
LinkedList:按性能由高到低排序: Iterator > 增强for > forEach> for
ArrayList测试
package org.example.a;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo {
public static void main(String[] args) {
Object obj = new Object();
List<Object> list = new ArrayList<>();
int objCount = 1000;
for (int i = 0; i < objCount; i++) {
list.add(obj);
}
int testTimes = 5;
for (int i = 0; i < testTimes; i++) {
testFor(list);
testForEnhanced(list);
testForEach(list);
testIterator(list);
System.out.println();
}
}
private static void testFor(List<Object> list) {
long startTime = 0L;
long endTime = 0L;
startTime = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
}
endTime = System.nanoTime();
System.out.println("for所用时间(ns) :" + (endTime - startTime));
}
private static void testForEnhanced(List<Object> list) {
long startTime = 0L;
long endTime = 0L;
startTime = System.nanoTime();
for (Object o : list) {
Object value = o;
}
endTime = System.nanoTime();
System.out.println("增强for所用时间(ns) :" + (endTime - startTime));
}
private static void testForEach(List<Object> list) {
long startTime = 0L;
long endTime = 0L;
startTime = System.nanoTime();
list.forEach(o->{Object obj = o;});
endTime = System.nanoTime();
System.out.println("forEach所用时间(ns) :" + (endTime - startTime));
}
private static void testIterator(List<Object> list) {
long startTime = 0L;
long endTime = 0L;
startTime = System.nanoTime();
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
}
endTime = System.nanoTime();
System.out.println("iterator所用时间(ns) :" + (endTime - startTime));
}
}
结果
for所用时间(ns) :619500
增强for所用时间(ns) :572300
forEach所用时间(ns) :121775301
iterator所用时间(ns) :34700
for所用时间(ns) :36799
增强for所用时间(ns) :36000
forEach所用时间(ns) :24300
iterator所用时间(ns) :33200
for所用时间(ns) :41401
增强for所用时间(ns) :37800
forEach所用时间(ns) :23799
iterator所用时间(ns) :44100
for所用时间(ns) :34400
增强for所用时间(ns) :345101
forEach所用时间(ns) :24300
iterator所用时间(ns) :31200
for所用时间(ns) :34299
增强for所用时间(ns) :33100
forEach所用时间(ns) :23099
iterator所用时间(ns) :30901
第一次测试:性能由高到低排序: Iterator > 增强for > for > forEach
第二次之后:性能由高到低排序:forEach > Iterator > 增强for > for
原因:
- java8的foreach预热是jvm级别的,需要预热。
- 增强for编译之后是基于Iterator的,所以会比直接用Iterator稍微慢一点点。(写一个增强for循环代码,然后反编译,即可发现它是迭代器方式)
LinkedList测试
将上边测试代码中的ArrayList改为LinkedList,其他不变。
结果:
for所用时间(ns) :1399300
增强for所用时间(ns) :1391200
forEach所用时间(ns) :57331500
iterator所用时间(ns) :44099
for所用时间(ns) :940200
增强for所用时间(ns) :38701
forEach所用时间(ns) :71100
iterator所用时间(ns) :60600
for所用时间(ns) :976100
增强for所用时间(ns) :32301
forEach所用时间(ns) :46000
iterator所用时间(ns) :28700
for所用时间(ns) :358901
增强for所用时间(ns) :37100
forEach所用时间(ns) :42401
iterator所用时间(ns) :29000
for所用时间(ns) :358100
增强for所用时间(ns) :30500
forEach所用时间(ns) :41499
iterator所用时间(ns) :28800
去重
简介
去重一共有这几种方法(按推荐顺序排列):
- JDK8的stream的distinct
- 转为HashSet(分不保持顺序和保持顺序两种)
- 转为TreeSet
- 使用retainAll/removeAll、contains、equals等基本方法
直接去重
package com.example.a;
import java.util.*;
import java.util.stream.Collectors;
public class Demo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
list.add(3);
System.out.println("源数据: " + list);
System.out.println("stream: " + stream(list));
System.out.println("hashSetWithoutOrder:" + hashSetWithOrder(list));
System.out.println("hashSetWithOrder: " + hashSetWithOrder(list));
System.out.println("treeSet: " + treeSet(list));
}
//JDK1.8的stream去重
private static List<Integer> stream(List<Integer> list) {
return list.stream().distinct().collect(Collectors.toList());
}
//HashSet(不保持顺序)
private static List<Integer> hashSetWithoutOrder(List<Integer> list) {
HashSet<Integer> h = new HashSet<Integer>(list);
return new ArrayList<>(h);
}
// 删除ArrayList中重复元素,保持顺序
public static List<Integer> hashSetWithOrder(List<Integer> list) {
Set<Integer> set = new HashSet<Integer>();
List<Integer> newList = new ArrayList<Integer>();
for (Integer element : list) {
if (set.add(element))
newList.add(element);
}
return newList;
}
//TreeSet(自动排序)
public static List<Integer> treeSet(List<Integer> list) {
TreeSet<Integer> set = new TreeSet<Integer>(list);
return new ArrayList<>(set);
}
}
执行结果
源数据: [1, 3, 2, 3]
stream: [1, 3, 2]
hashSetWithoutOrder:[1, 3, 2]
hashSetWithOrder: [1, 3, 2]
treeSet: [1, 2, 3]
根据对象属性去重
法1:TreeSet
Set<User> setByName = new TreeSet<User>((o1, o2) ->
o1.getName().compareTo(o2.getName()));
setByName.addAll(list);
List<User> listByName = new ArrayList<>(setByName);
System.out.println(listByName);
//[User{name='Pepper', age=20, Phone='123'}, User{name='Tony', age=20, Phone='12'}]
Set<User> setByNameAndAge = new TreeSet<User>((o1, o2) -> {
return (o1.getName() + o1.getAge()).compareTo((o2.getName() + o2.getAge()));
// return o1.getName().compareTo(o2.getName()) == 0
// ? o1.getAge().compareTo(o2.getAge())
// : o1.getName().compareTo(o2.getName());
});
setByNameAndAge.addAll(list);
List<User> listByNameAndAge = new ArrayList<>(setByNameAndAge);
System.out.println(listByNameAndAge);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'},
// User{name='Tony', age=22, Phone='1234'}]
stream+TreeSet
List<User> streamByNameList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new
));
System.out.println(streamByNameList);
//[User{name='Pepper', age=20, Phone='123'}, User{name='Tony', age=20, Phone='12'}]
List<User> streamByNameAndAgeList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + o.getAge()))), ArrayList::new
));
System.out.println(streamByNameAndAgeList);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'},
// User{name='Tony', age=22, Phone='1234'}]
所有代码
package org.example.a;
import java.util.*;
import java.util.stream.Collectors;
class User {
private String name;
private Integer age;
private String Phone;
public User(String name, Integer age, String phone) {
this.name = name;
this.age = age;
Phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getPhone() {
return Phone;
}
public void setPhone(String phone) {
Phone = phone;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", Phone='" + Phone + '\'' +
'}';
}
}
public class Demo {
public static void main(String[] args) {
List<User> list = new ArrayList<User>() {{
add(new User("Tony", 20, "12"));
add(new User("Pepper", 20, "123"));
add(new User("Tony", 22, "1234"));
}};
Set<User> setByName = new TreeSet<User>((o1, o2) ->
o1.getName().compareTo(o2.getName()));
setByName.addAll(list);
List<User> listByName = new ArrayList<>(setByName);
System.out.println(listByName);
//[User{name='Pepper', age=20, Phone='123'}, User{name='Tony', age=20, Phone='12'}]
Set<User> setByNameAndAge = new TreeSet<User>((o1, o2) -> {
return (o1.getName() + o1.getAge()).compareTo((o2.getName() + o2.getAge()));
// return o1.getName().compareTo(o2.getName()) == 0
// ? o1.getAge().compareTo(o2.getAge())
// : o1.getName().compareTo(o2.getName());
});
setByNameAndAge.addAll(list);
List<User> listByNameAndAge = new ArrayList<>(setByNameAndAge);
System.out.println(listByNameAndAge);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'},
// User{name='Tony', age=22, Phone='1234'}]
List<User> streamByNameList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new
));
System.out.println(streamByNameList);
//[User{name='Pepper', age=20, Phone='123'}, User{name='Tony', age=20, Phone='12'}]
List<User> streamByNameAndAgeList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + o.getAge()))), ArrayList::new
));
System.out.println(streamByNameAndAgeList);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'},
// User{name='Tony', age=22, Phone='1234'}]
}
}
List排序
主要有三种方法(按推荐度排序):
- JDK8的stream
- Comparator#compare()
- Comparable#compareTo()
法1:JDK8的stream
法2:Comparator#compare()
需求:用户有成绩和年龄。按成绩排序,若成绩相同,则按年龄排序。
package org.example.a;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class User{
private int score;
private int age;
public User(int score, int age){
super();
this.score = score;
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Demo {
public static void main(String[] args) {
List<User> users = new ArrayList<User>();
users.add(new User(95, 26));
users.add(new User(84, 23));
users.add(new User(96, 25));
users.add(new User(95, 24));
Collections.sort(users, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
int i = o2.getScore() - o1.getScore();
if(i == 0){
return o1.getAge() - o2.getAge();
}
return i;
}
});
for(User user : users){
System.out.println(user.getScore() + "," + user.getAge());
}
}
}
执行结果
96,25
95,24
95,26
84,23
法3:Comparable#compareTo()
默认按增序排序:
package org.example.a;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(5);
nums.add(1);
nums.add(0);
System.out.println(nums);
Collections.sort(nums);
System.out.println(nums);
}
}
执行结果:
[3, 5, 1, 0]
[0, 1, 3, 5]
需求:用户有成绩和年龄。按成绩降序排序,若成绩相同,则按年龄正序排序。
package org.example.a;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class User implements Comparable<User>{
private int score;
private int age;
public User(int score, int age){
super();
this.score = score;
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(User o) {
int i = o.getScore() - this.getScore();
if(i == 0){
return this.getAge() - o.getAge();
}
return i;
}
}
public class Demo {
public static void main(String[] args) {
List<User> users = new ArrayList<User>();
users.add(new User(95, 26));
users.add(new User(84, 23));
users.add(new User(96, 25));
users.add(new User(95, 24));
Collections.sort(users);
for(User user : users){
System.out.println(user.getScore() + "," + user.getAge());
}
}
}
执行结果
96,25
95,24
95,26
84,23
安全删除
需求:有如下初始数据,将list中的所有数据为"b"的元素删除掉。即:填充removeB()方法
package com.example.a;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d");
removeB(list);
}
private static void removeB(List<String> list) {
// 待填充
}
}
正确方法
法1:for的下标倒序遍历
private static void removeB(List<String> list) {
for (int i = list.size() - 1; i >= 0; i--) {
if ("b".equals(list.get(i))) {
list.remove(i);
}
}
System.out.println(list);
}
结果
[a, c, d]
法2: list.stream().filter().collect()
private static void removeB(List<String> list) {
List<String> newList = list.stream()
.filter(e -> !"b".equals(e))
.collect(Collectors.toList());
System.out.println(newList);
}
结果
[a, c, d]
法3: iterator迭代器
private static void removeB(List<String> list) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s)) {
it.remove();
}
}
System.out.println(list);
}
结果
[a, c, d]
错误方法
法1:for(xxx : yyy)遍历
private static void removeB(List<String> list) {
for (String s : list) {
if ("b".equals(s)) {
list.remove(s);
}
}
System.out.println(list);
}
结果(报异常)
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.example.a.Demo.removeB(Demo.java:18)
at com.example.a.Demo.main(Demo.java:14)
法2:for的下标正序遍历
private static void removeB(List<String> list) {
for (int i = 0; i < list.size(); i++) {
if ("b".equals(list.get(i))) {
list.remove(list.get(i));
}
}
System.out.println(list);
}
结果(有的没有删除掉)
[a, b, c, d]
原因分析
需求:通过遍历把list集合的所有数据删除掉。
package com.example.a;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
public class Demo {
private static List<String> list = new ArrayList<>();
public static void main(String[] args) {
reset();
// method1();
// method2();
// method3();
// method4();
}
private static void reset(){
list.clear();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
}
/**
* 方法一:遍历删除第i个元素(错误版)
* 执行结果:没有完全删除。打印结果:[b, d]
*/
/**
* 下面遍历操作过程如下:
* i = 0 list.size() == 5 执行完第一次list.remove(0); list剩下[b,c,d,e]
* i = 1 list.size() == 4 执行完第二次list.remove(1); list剩下[b,d,e]
* i = 2 list.size() == 3 执行完第三次list.remove(2); list剩下[b,d]
* i = 3 list.size() == 2 i > list.size()所以条件不满足,最后剩下[b,d]
*/
public static void method1() {
for (int i = 0; i< list.size(); i++) {
list.remove(i);
}
System.out.println(list);
}
/**
* 方法二:删除第0个元素(错误版)
* 执行结果:元素没有删除。打印结果:[d, e]
*/
/**
* 下面遍历操作过程如下:
* i = 0 list.size() == 5 执行完第一次list.remove(0); list剩下[b,c,d,e]
* i = 1 list.size() == 4 执行完第二次list.remove(0); list剩下[c,d,e]
* i = 2 list.size() == 3 执行完第三次list.remove(0); list剩下[d,e]
* i = 3 list.size() == 2 i > list.size()所以条件不满足,最后剩下[d,e]
*/
public static void method2() {
for (int i = 0; i< list.size(); i++) {
list.remove(0);
}
System.out.println(list);
}
/**
* 方法三:只取一次长度删除第i个(错误版)
* 执行结果:报错。打印结果:
* Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 2
*/
/**
* 只求取list.size()长度一次
* i == 0 len == 5 list.remove(0) list剩下[b,c,d,e]
* i == 1 len == 5 list.remove(1) list剩下[b, d,e]
* i == 2 len == 5 list.remove(2) list剩下[b, d]
* i == 3 len == 5 list.remove(3) list因为没有第四个元素,于是报索引越界错误
*/
public static void method3() {
int len = list.size();
for (int i = 0; i < len; i++) {
list.remove(i);
}
System.out.println(list);
}
/**
* 方法四:只取一次长度删除第0个(正确版)
* 执行结果:完全删除。打印结果:[]
*/
/**
* 下面遍历操作过程如下:
* i = 0 len == 5 执行完第一次list.remove(0); list剩下[b,c,d,e]
* i = 1 len == 5 执行完第二次list.remove(0); list剩下[c,d,e]
* i = 2 len == 5 执行完第三次list.remove(0); list剩下[d,e]
* i = 3 len == 5 执行完第四次list.remove(0); list剩下[e]
* i = 4 len == 5 执行完第五次list.remove(0); list剩下[]
*/
public static void method4() {
int len = list.size(); // 保证只获取一次长度
for (int i = 0; i< len; i++) {
list.remove(0);
}
System.out.println(list);
}
}
其他网址
在 Java 中初始化 List 的五种方法
遍历
去重