项目中经常会通过集合的contains方法来检查某个对象是否存在。ArrayList是集合中使用频率最高的,因此大家习惯性的会直接使用ArrayList的contains方法做检查。
当元素数量较少时对性能没有任何影响,但当操作上十万百万元素时,性能问题便凸显出来。ArrayList的contains方法效率非常低下,此时我们最简单的优化方案便是
使用HashSet的contains方法取代,下方是测试代码,通过最终的测试结果显示,两者的效率差距上百倍。
分析其原理:
ArraysList的contains方法是采用indexOf遍历数据的方式判断存在性,当元素非常多时,这种方式方式非常低,时间复杂度为o(n);
HashSet的contains方法是采用HashMap的containsKey方法,根据key计算hash值,然后取对应链表判断存在性,时间复杂度为o(1),效率比ArrayList高很多。
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.junit.Assert;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ContainsTest {
public static void main(String[] args) {
int COUNT = 20000000;
List<Element> elementList = new ArrayList<>(COUNT);
Set<Element> elementSet = new HashSet<>(COUNT);
for (int i = 0; i < COUNT; i++) {
Element element = new Element(i, "name" + i);
elementList.add(element);
elementSet.add(element);
}
//
Element element = new Element(COUNT - 1, "name" + (COUNT - 1));
Instant start = null;
boolean contains = false;
//
start = Instant.now();
contains = elementSet.contains(element);
Assert.assertTrue(contains);
System.out.println(String.format("set contains method elapsed time[%s]ms", Duration.between(start, Instant.now()).toMillis()));
//
start = Instant.now();
contains = elementList.contains(element);
Assert.assertTrue(contains);
System.out.println(String.format("list contains method elapsed time[%s]ms", Duration.between(start, Instant.now()).toMillis()));
}
static class Element {
int id;
String name;
public Element(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Element element = (Element) o;
return new EqualsBuilder()
.append(id, element.id)
.append(name, element.name)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(id)
.append(name)
.toHashCode();
}
}
}
set contains method elapsed time[2]ms
list contains method elapsed time[334]ms