集合
1、Collection接口
Collection层次结构中的根接口。Collection表示一组对象,这些对象也称为collection的元素。一些collection允许有重复的元素,而另一些则不允许。一些collection是有序的,而另一些则是无序的。JDK不提供此接口的任何直接实现:它提供更具体的子接口(如Set和List)实现。此接口通常用来传递collection,并在需要最大普遍性的地方操作这些collection。
2、List接口
List接口是有序的collection,此接口的用户可以对列表中每个元素的插入位置进行精确的控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
ArrayList:
(1)实现原理,采用动态对象数组实现,默认构造方法创建了一个空数组。
(2)第一次添加元素,扩展容量是10,之后的扩充算法:原来数组大小+原来数组的一半。
(3)不适合进行删除或插入操作。
(4)为了防止数组动态扩充次数太多,建议创建ArrayList时,给定初始容量。
(5)线程不安全,适合在单线程访问时使用。
Vector:
(1)实现原理,采用动态对象数组实现,默认构造方法创建了一个大小为10的对象数组。
(2)扩充算法:当增量为0时,扩充为原大小的2倍,当增量大于0时,扩充为原来大小+增量。
(3)不适合进行删除或插入操作。
(4)为了防止数组动态扩充次数太多,建议创建ArrayList时,给定初始容量。
(5)线程不安全,适合在多线程访问时使用,效率较低。
LinkedList:
(1)实现原理,采用双向链表结构实现。
(2)适合插入,删除操作,性能高。
在实际开发中,如何选择list的具体实现?
(1)安全性问题:Vector/ArrayList
(2)是否频繁插入,删除操作:LinkedList
(3)是否存储后遍历:ArraysList
import java.util.List;
import java.util.ArrayList;
public class Test01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
arrayList();
}
private static void arrayList(){
List<String> list=new ArrayList<>();//只计算String类型
list.add("1");
list.add("2");
list.add("3");
list.add("4");
// list.add(10,null);
int size=list.size();
for(int i=0;i<size;i++){
System.out.println(list.get(i));
}
System.out.println(list.contains("1"));//判断是否包含“1”
list.remove("2");//删除
System.out.println(list.size());//输出list大小
String[] array=list.toArray(new String[]{});//转为String类型
for(String x:array){
System.out.println(x);
}
}
private static void vector(){
Vector<String> v=new Vector<>();//只计算String类型
v.add("1");
v.add("2");
v.add("3");
v.add("4");
int size=v.size();
for(int i=0;i<size;i++){
System.out.println(v.get(i));
}
}
private static void linkedList(){
LinkedList list=new LinkedList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
int size=list.size();
for(int i=0;i<size;i++){
System.out.println(list.get(i));
}
}
}
3、Set接口
无序、一个不包含重复元素的collection,并且最多包含一个null元素。
HashSet:
(1)实现原理,基于哈希表(HashMap)实现。
哈希表的存储结构:数组+链表,数组里的每个元素以链表的形式存储。
把对象存储到哈希表中,先计算对象的HashCode值,再对数组的长度求余数,来决定对象要存储在数组中的哪个位置。
(2)不保证顺序恒久不变。
(3)添加元素时,把元素作为HashMap的Key存储,HashMap的value使用一个固定的object对象。
(4)排除重复元素,通过equals检查对象是否相同。
(5)在Java集合中判断两个对象是否相等的规则是:
判断两个对象的HashCode是否相等,不相等结束,相等转入2
判断两个对象用equals运算是否相等,不相等结束,相等则认为两个对象相等。
(6)自定义对象要认为属性值相同时为同一个对象,则需要重写所在对象的类的HashCode和equals方法。
import java.util.*;
public class Test02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// hashSet();
pet();
}
private static void hashSet(){
Set<String> set=new HashSet<>();
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("3");
String[] str=set.toArray(new String[]{});
for(String x:str){
System.out.println(x);
}
System.out.println(set.size());
}
private static void pet(){
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",1);
Pet pet3=new Pet("33",1);
Pet pet4=new Pet("44",1);
Set<Pet> set=new HashSet<>();
set.add(pet1);
set.add(pet2);
set.add(pet3);
set.add(pet4);
set.add(pet3);
System.out.println(set.size());
System.out.println(pet1.hashCode());
System.out.println(pet2.hashCode());
System.out.println(pet3.hashCode());
System.out.println(pet4.hashCode());
}
}
class Pet{
private String name;
private int 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 "Pet [name=" + name + ", age=" + age + ", getName()=" + getName() + ", getAge()=" + getAge()
+ ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString()
+ "]";
}
public Pet(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Pet() {
super();
// TODO Auto-generated constructor stub
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pet other = (Pet) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
TreeSet:
有序的,基于TreeMap(二叉树数据结构),对象需要比较大小,通过对象比较器来实现。
对象比较器还可以用来去除重复元素,如果自定义的数据类,没有实现比较器接口,将无法添加到TreeSet集合中。
private static void TreeSet(){
TreeSet<Pet> ts=new TreeSet<>(new PetComparator());//比较器可以判断重复还可以排序
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",3);
Pet pet3=new Pet("33",2);
Pet pet4=new Pet("44",1);
ts.add(pet1);
ts.add(pet2);
ts.add(pet3);
ts.add(pet4);
System.out.println(ts.size());//按照年龄比较,输出1
for(Pet p:ts){
System.out.println(p);
}
}
class PetComparator implements Comparator<Pet>{
@Override
public int compare(Pet o1, Pet o2) {
// TODO Auto-generated method stub
return o1.getAge()-o2.getAge();
}
LinkedHashSet:
哈希表和链接列表实现,维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照元素将元素插入到set的顺序进行迭代。
private static void LinkedHashSet(){
LinkedHashSet<Pet> lhs=new LinkedHashSet<>();
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",3);
Pet pet3=new Pet("33",2);
Pet pet4=new Pet("44",1);
lhs.add(pet1);
lhs.add(pet2);
lhs.add(pet3);
lhs.add(pet4);
System.out.println(lhs.size());//按照年龄比较,输出1
for(Pet p:lhs){
System.out.println(p);
}
}
在实际开发中,如何选择Set的具体实现?
如果要排序,选择TreeSet
如果不需要排序,也不用保证顺序,选择HashSet
如果不需要排序,要保证顺序,选择LinkedHashSet
4、Iterator/Enumeration/foreach接口
遍历集合的方式。
//Iterator
public static void main(String[] args) {
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",3);
Pet pet3=new Pet("33",2);
Pet pet4=new Pet("44",1);
List<Pet> list=new ArrayList<>();
list.add(pet1);
list.add(pet2);
list.add(pet3);
list.add(pet4);
iterator(list);
}
public static void iterator(Collection c){
Iterator<Pet> i=c.iterator();
while(i.hasNext()){
System.out.println(i.next());
}
}
//Enumeration
public static void enumeration(){
Vector<Pet> v=new Vector();
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",3);
Pet pet3=new Pet("33",2);
Pet pet4=new Pet("44",1);
v.add(pet1);
v.add(pet2);
v.add(pet3);
v.add(pet4);
Enumeration<Pet> em=v.elements();
while(em.hasMoreElements()){
System.out.println(em.nextElement());
}
}
//foreach
public static void foreach(){
List<Pet> list=new ArrayList<>();
Pet pet1=new Pet("11",1);
Pet pet2=new Pet("22",3);
Pet pet3=new Pet("33",2);
Pet pet4=new Pet("44",1);
list.add(pet1);
list.add(pet2);
list.add(pet3);
list.add(pet4);
list.forEach((Pet p)->{System.out.println(p);});//Consumer<T>
list.forEach(System.out::println);
}
5、JDK1.8新特性
Consumer 消费者接口
Function<T,R> 表示接受一个参数并产生结果的函数
Supplier 代表结果供应商
Predicate 断言接口
Stream接口
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Stream<String> stream=Stream.of("day","day","up","up");
// stream.forEach((str)->{System.out.println(str);});
// stream.forEach(System.out::println);
// stream.filter((s)->s.length()>2).forEach(System.out::println);//过滤
// stream.distinct().forEach((s)->System.out.println(s));//去除重复
// stream.map(s->s.toUpperCase()).forEach(System.out::println);;//转为大写输出
// Stream<List<Integer>> ss=Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5));//添加
// ss.flatMap(list->list.stream()).forEach(System.out::println);;
// Optional<String> opt=stream.reduce((s1,s2)->s1.length()>s2.length()?s1:s2);
// System.out.println(opt.get());
List<String> list=stream.collect(Collectors.toList());
list.forEach(s->System.out.println(s));
}
}
6、Map接口
将键映射到值。键值对存储一组对象,Key不能重复,Value可以重复。具体的实现类:HashMap、TreeMap、HashTable、LinkedHashMap
HashMap的实现原理:
(1)基于哈希表(数组+链表+红黑树)。
(2)默认加载因子为0.75,默认数组大小是16。
(3)如何把对象存储到哈希表中?
把Key对象通过Hash()方法计算hash值,然后用这个hash值对数组长度取余数(默认16),来决定该Key对象在数组中存储的位置,当这个位置有多个对象时,以链表结构存储,JDK1.8之后,当链表长度大于8时,链表将转换为红黑树结构存储。这样的目的,是为了取值更快,存储的数据量越大,性能的表现越明显。
(4)扩充原理: 当数组的容量超过了75%,那么该数组需要扩充。
**(5)扩充算法: **当前数组的容量<<1(相当于乘2),扩大1倍,扩充次数过多,会影响性能,每次扩充表示哈希表重新散列(重新计算每个对象的存储位置),我们在开发过程中尽量要减少扩充次数带来的性能问题。
(6)线程不安全,适合在单线程中使用。
//HashMap
import java.security.*;
import java.util.*;
import java.util.Map.Entry;
public class Test01 {
//Map
public static void main(String[] args) {
// TODO Auto-generated method stub
Hashmap();
}
private static void Hashmap(){
Map<Integer,String> map=new HashMap();
map.put(1, "一");
map.put(2, "二");
map.put(3, "三");
map.put(4, "四");
System.out.println(map.size());
System.out.println(map.get(2));//根据键取值
Set<Entry<Integer,String>> entry=map.entrySet();
for(Entry e:entry){
System.out.println(e.getKey()+"->"+e.getValue());//遍历1
}
Set<Integer> keys=map.keySet();
for(Integer i:keys){
String str=map.get(i);
System.out.println(i+"->"+str);//遍历2
}
Collection<String> values=map.values();
for(String str:values){
System.out.println(values);//遍历3,只遍历值
}
map.forEach((key,value)->{System.out.println(key+"->"+value);});//遍历4
}
}
Hashtable:
(1)基于哈希表实现(数组+链表)。
(2)默认数组大小为11,加载因子0.75。
(3)扩充方式:原数组大小<<1+1。
(4)线程安全,适合多线程使用。
private static void Hashtable(){
Map<Integer,String> m=new Hashtable();
m.put(1, "一");
m.put(2, "二");
m.put(3, "三");
m.put(4, "四");
m.forEach((key,value)->{System.out.println(key+"->"+value);});
}
LinkedHashMap:
LinkedHashMap是HashMap的子类,由于HashMap不能保证顺序恒久不变,此类使用一个双重链表来维护元素添加的顺序。
使用方法参考Hashtable
TreeMap:
基于二叉树的红黑数实现,有序。
JDK1.8新特性
private static void hashmap(){
Map<Integer,String> map=new HashMap();
map.put(1, "11");
map.put(2, "22");
map.put(3, "33");
String str=map.getOrDefault(3, "null");//不需另外判断是否为空
System.out.println(str);
map.putIfAbsent(3, "333");//若key=3为空则填充
map.forEach((key,value)->{System.out.println(value);});
map.replace(1, "111");//替换key=1的value
System.out.println(map.get(1));
map.compute(1, (k,v)->v+"1");//在key=1的value后拼接一个“1”
System.out.println(map.get(1));
map.merge(1, "23", (oldVal,newVal)->newVal.concat(oldVal));//拼接新的内容,key不存在就直接填入新的value
System.out.println(map.get(1));
}
7、Collections工具类
private static void list(){
List<String> l=new ArrayList();
l.add("1");
l.add("2");
l.add("3");
l.add("4");
l.add("5");
Collections.reverse(l);//翻转
Collections.shuffle(l);//打乱顺序
Collections.sort(l);//排序
Collections.swap(l, 1, 2);//交换
Collections.rotate(l, 2);//旋转
System.err.println(Collections.binarySearch(l, "3"));
System.out.println(Collections.frequency(l, "1"));
Collections.replaceAll(l, "4", "6");
List<String> synlist=Collections.synchronizedList(new ArrayList<String>());//线程同步
List<String> elist=Collections.emptyList();//return一个空集合,避免空指针
}
8、Optional容器类
private static void optional(){
//创建Optional对象
Optional<String> opt=Optional.of("opt");
Optional<String> opt1=Optional.empty();
Optional<String> opt2=Optional.ofNullable("opt");
System.out.println(opt.isPresent());//判断空指针
System.out.println(opt.get());//输出
opt1.ifPresent((value)->System.out.println(value));//若存在值就返回,没值不返回
System.out.println(opt1.orElse("null"));//无值返回null
// try {
// opt1.orElseThrow(Exception::new);//不存在则抛出一个异常
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
Optional o=opt.map((value)->value.toUpperCase());//使用mapping方法
System.out.println(o);
opt=opt.flatMap((value)->Optional.of(value.toUpperCase()+"flatMap"));
System.out.println(opt.orElse("null"));
opt.filter((value)->value.length()>1);
}
9、Queue(队列)、Deque(双端队列)接口、Stack(栈)类
LinkedList实现了Queue接口
private static void queue(){
Queue q=new LinkedList();
q.add("1");
q.add("2");
q.add("3");
q.add("4");
System.out.println(q.size());
System.out.println(q.peek());//队头
System.out.println(q.size());
System.out.println(q.poll());//出队
System.out.println(q.size());
}
LinkedList实现了Deque接口
private static void deque(){
Deque<String> d=new LinkedList<>();
d.add("1");
d.add("2");
d.add("3");
d.add("4");
System.out.println(d.getFirst());//队头
System.out.println(d.getLast());//队尾
}
Stack类
private static void stack(){
Stack<String> s=new Stack<>();
s.push("1");
s.push("2");
System.out.println(s);
s.pop();
System.out.println(s);
}