Java集合
集合分为三大接口:Collection
,Map
,Iterator
Collection
用于存储单个对象的集合,其下有两个子接口:List
,Set
List接口
- 有序接口
- 允许有多个null元素,元素可以重复
- 具体的实现有常用的:
ArrayList
,Vector
,LinkedList
ArrayList
采用动态数组实现,默认构造方法是创建一个空数组
添加时每次都扩充一个单位
不适合进行删除和插入
为了防止数组动态扩充次数过多,可以在创建时给定初始容量
线程不安全,适合在单线程访问时使用,效率较高
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
arrayList();
}
private static void arrayList() {
List list = new ArrayList();//可以使用集合来存储多个不同类型的元素(对象)
List<String> list1 = new ArrayList<String>();////可以使用泛型指定对象类型
//数组添加
list.add("笑一笑,十年少");
list.add(123);
list1.add("飞驰人生");
list1.add("流浪地球");
//list1.add(123);//报错
int size = list1.size();
for(int i=0;i<size;i++) {
System.out.println(list.get(i));
System.out.println(list1.get(i));
}
//判断是否包含
System.out.println(list1.contains("飞驰人生"));
//删除
list1.remove("飞驰人生");
System.out.println(list1.size());
//转成数组
String[] array = list1.toArray(new String[] {});
for(String s: array) {
System.out.println(s);
}
}
}
Vector
采用动态数组实现,默认构造方法是创建一个容量为10数组
添加时每次都扩充一个单位
不适合进行删除和插入
为了防止数组动态扩充次数过多,可以在创建时给定初始容量
线程安全,适合在多线程访问时使用,单效率较低 (区别)
private static void vector() {
Vector<String> v = new Vector<String>();
v.add("昨夜风疏雨");
v.add("浓睡不消残酒");
for(int i=0;i<v.size();i++) {
System.out.println(v.get(i));
}
}
LinkedList
采用双向链表的数据结构
适合插入,删除操作,性能高
private static void linkedList() {
LinkedList<String> ll = new LinkedList<String>();
ll.add("试问卷帘人");
ll.add("却道海棠花依旧");
for(int i=0;i<ll.size();i++) {
System.out.println(ll.get(i));
}
}
Set接口
- 无序的
- 不允许元素重复
- 具体的实现类有:
HashSet
,TreeSet
,LinkedHashSet
HashSet
实现原理:哈希表
不允许重复(通过equals检查是否相同),可以有一个null元素
不保证顺序恒久不变
添加元素把元素作为HashMap的key存储,HashMap的value使用一个固定的Object对象
private static void hashSet() {
Set<String> set = new HashSet<String>();
set.add("知否,");
set.add("知否,");//相同会自动替换掉上面的
set.add("知否 ");
set.add("应是绿肥红瘦");
String[] names = set.toArray(new String[] {});
for(String s:names) {
System.out.print(s);
}
}
hashCode
是一个定义在object类中的本地方法,它的实现与本地机器相关
判断两个对象是否相等,首先是判断两个对象的hashCode
是否相等,如果相等,进一步进行equals比较,都相同则是同一个对象
若要求自定义对象的属性值一致时认为是同一个对象,则可以重写所在类的hashCode
和equals
方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((sex == null) ? 0 : sex.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;
Dog other = (Dog) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (sex == null) {
if (other.sex != null)
return false;
} else if (!sex.equals(other.sex))
return false;
return true;
}
TreeSet
TreeSet是有序的,基于二叉树数据结构,对象需要有比较器
对象比较器还可以用来去除重复元素
private static void treeSet() {
Set<String> tree = new TreeSet<String>();
tree.add("任侠平生愿");
tree.add("一叶边舟莲波滟");
//若是自定义的类,需要实现comparator接口(对象比较器),因为treeSet实现二叉树有序排列
Set<Cat> catTree = new TreeSet<Cat>(new CatComparator());
Cat c1 = new Cat(1,"喵喵");
Cat c2 = new Cat(2,"旺旺");
Cat c3 = new Cat(0,"嘟嘟");
//Cat c4 = new Cat(1,"Tom");//因为age属性相同,执行add方法不会被加入(去除重复元素)
catTree.add(c1);
catTree.add(c2);
catTree.add(c3);
System.out.println(catTree.size());
}
LinkedHashSet
根据添加顺序排序,相同元素添加会改变原有顺序
private static void linkedHashSet() {
Set<Cat> linkHash = new LinkedHashSet()<Cat>;
Cat c1 = new Cat(1,"喵喵");
Cat c2 = new Cat(2,"旺旺");
Cat c3 = new Cat(0,"嘟嘟");
linkHash.add(c1);
linkHash.add(c2);
linkHash.add(c3);
System.out.println(catTree.size());
}
Iterator(迭代器)
用于遍历集合
三种迭代方式
//foreach迭代
private static void foreach(Collection<Cat> c) {
for(Cat cat :c) {
System.out.println(cat);
}
}
//Iterator
private static void iterator(Collection<Cat> c) {
Iterator<Cat> iter = c.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
}
private static void enumration() {
Vector<String> vs = new Vector<String>();
vs.add("tom");
vs.add("jack");
vs.add("job");
vs.add("lily");
Enumeration<String> es = vs.elements();
while(es.hasMoreElements()) {
System.out.println(es.nextElement());
}
}
foreach接口
JDK1.8实现了forEach方法
private static void foreach() {
List<String> list = new ArrayList<String>();
list.add("tom");
list.add("jack");
list.add("job");
list.add("lily");
//多种输出方式
list.forEach(s->System.out.println(s));
list.forEach((String s)->{System.out.println(s);});
list.forEach(s->{System.out.println(s);});
list.forEach(System.out::print);
}
四大核心接口
Consumer<T> 消费者接口
Function<T,R> 表示接受一个参数并产生结果的函数
Supplier<T> 代表结果供应商
Predicater<T> 断言接口
private static void functionTest() {
String s = strToUpp("abcdefg",(str)->str.toUpperCase());
System.out.println(s);
}
private static String strToUpp(String str,Function<String, String> f) {
return f.apply(str);
}
public static void supplierTest() {
List<Integer> list = getNums(10,()->(int)(Math.random()*100));
list.forEach(System.out::println);
}
public static List<Integer> getNums(int num,Supplier<Integer> sup){
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<num;i++) {
list.add(sup.get());
}
return list;
}
public static void predicateTest() {
List<String> list = Arrays.asList("AEG","MAC","Landom","eesg");
List<String> result = filter(list,(s)->s.contains("a"));
result.forEach(System.out::println);
}
public static List<String> filter(List<String> list,Predicate<String> p){
List<String> results = new ArrayList<String>();
for(String s: list) {
if(p.test(s)) {
//测试是否包含
results.add(s);
}
}
return results;
}
Stream接口
Stream接口的数据源是一个集合,为了函数式编程创造,惰式执行,数据只能被消费一次(使用一次)
两种操作类型:
- 中间操作(生成一个Stream),可以再调用别的Stream接口的方法
- 结束操作(执行计算操作)
package Collection;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
//of方法用于生成Stream
Stream<String> stream = Stream.of("good","good","study","day","day","up");
//foreach方法(结束操作)
stream.forEach((str)->System.out.println(str));
stream.forEach(System.out::println);
//过滤filter方法(中间操作)
stream.filter((s)->s.length()>3).forEach(System.out::println);
//去除重复值
stream.distinct().forEach(System.out::println);
//映射map(把流全部映射后再进行操作)
stream.map(s->s.toUpperCase()).forEach(System.out::println);
//平摊flatMap(对于一个流中有不同的元素,每个元素有不同数量的集合,通过该方法把所有集合整合在一个流中)
Stream<List<Integer>> ss = Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5));
ss.flatMap(list->list.stream()).forEach(s->System.out.println(s));
//reduce
Optional<String> opt = stream.reduce((s1,s2)->s1.length()>=s2.length()?s1:s2);
System.out.println(opt.get());
//collect
List<String> list = stream.collect(Collectors.toList());
list.forEach(s->System.err.println(s));
}
}
关于::的用处
- 引用静态方法
- 引用对象的方法
- 引用构造方法
Map接口
- 键值对存储一组对象
- key要不能重复(唯一)
- value可以重复
具体的实现类有:HashMap
,TreeMap
,LinkedHashMap
HashMap
package Collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
hashMap();
}
private static void hashMap() {
Map<Integer, String> map = new HashMap<>();
//复制
map.put(1,"Tom");
map.put(2,"Jack");
map.put(3,"Zip");
//从map中取值
System.out.println(map.get(1));//通过key取value
//map遍历
Set<Entry<Integer, String>> entrySet = map.entrySet();
for(Entry e:entrySet) {
System.out.println(e.getKey()+"->"+e.getValue());
}
//第二种遍历方式:通过遍历键拿值
Set<Integer> keys = map.keySet();
for(Integer i:keys) {
String value = map.get(i);
System.out.println(i+"->"+value);
}
//第三种遍历方式(遍历值)
Collection<String> values = map.values();
for(String value:values) {
System.out.println(value);
}
//第四种:foreach
map.forEach((key,value)->System.out.println(key+"->"+value));
//判断是否包含
System.out.println(map.containsKey(6));
}
}
HashMap实现原理
- 基于哈希表(数组+链表+二叉树(红黑树))
- 默认加载因子为0.75,表示当数组的数据利用率达到75%就进行空间扩充,默认数组大小是16
- 把key对象通过hash()方法计算hash值,然后用hash值对数组长度取余数(默认16),来决定该key对象在数组中的存储位置,当该位置有多个对象时,以链表方式存储,当链表长度大于8时,链表转换为红黑树结构存储(提高性能)
- 当数组容量超过75%时,数据扩充一倍(左移1),扩充次数过多会影响性能(每次扩充哈希表会重新计算每个对象的存储位置),在开发中尽量减少扩充
- 线程不安全,适合单线程中使用
hashTable
- 基于哈希表实现
- 默认数组大小为11,加载因子为0.75
- 扩充方法为原数组左移1,再+1
- 线程安全,适合多线程使用
private static void hashtable() {
Map<String, String> table = new Hashtable<String, String>();
table.put("one", "Chinese");
table.put("two","English");
table.put("three","American");
table.forEach((key,value)->System.out.println(key+"->"+value));
}
LinkedHashMap
此类是HashMap的子类,由于HashMap不能保证顺序恒久不变,此类使用一个双重链表来维护元素的添加顺序
private static void linkedHashMap() {
Map<String, String> link = new LinkedHashMap<String, String>();
link.put("one", "Chinese");
link.put("two","English");
link.put("three","American");
link.forEach((key,value)->System.out.println(key+"->"+value));
}
TreeMap
基于红黑树实现,该映射根据键的顺序进行排序,对于自定义对象,根据Comparator进行排序,具体用法同上
Map接口jdk1.8新特性
Map接口中新增加了一些默认方法
//如果没有该值返回指定值
String name = map.getOrDefault(4, null);
//如果空才添加,put方法放回老的value,putIfAbsent返回新的或者已存在的
map.putIfAbsent(3, "xixi");
//删除,key和value都匹配时才能删除
map.remove(1, "vince");
//替换
map.replace(1, "aaa");//如果key不为空,替换为指定的value
map.replace(1, "jack", "youoyou");//键值对匹配才替换新的
//执行函数操作
map.compute(1,(k,v)->v+"1");//jack1
map.computeIfAbsent(2,(val)->val+"test");//为空就执行后面的函数
//合并
map.merge(1, "888", (oldVal,newVal)->oldVal.concat(newVal));//jack888
Collections工具类
该工具类提供了大量针对Collection/Map的操作,总体可以分为四类,都是静态方法
- 排序操作(主要针对List接口)
//反转
Collections.reverse(list);
//随机排序
Collections.shuffle(list);
//根据自然升序排序
Collections.sort(list);
//自定义比较器排序
Collections.sort(list,c);
//交换位置
Collections.swap(list, 0, 2);
//所有元素向右移动指定长度
Collections.rotate(list, 2);
- 查找和替换(针对Collection相关的接口)
//采用二分查找法,搜索返回键值(前提是已排序)
Collections.binarySearch(list, "tom");
//求最大最小值
Collections.max(list);
Collections.min(list);
//使用指定对象填充(全部变成指定对象)
Collections.fill(list, "duola");
//返回指定值出现次数
Collections.frequency(list, "lily");
//替换(替换所有跟指定值相同的值)
Collections.replaceAll(list, "old", "new");
- 同步控制
提供多个synchronizedXXX
方法,该方法返回指定集合对应的同步对象,针对HashSet,ArrayList,HashMap等线程不安全的同步问题
在使用迭代方法遍历集合时需要手工同步返回的集合
- 设置不可变的集合
- Collections.emptyXXX():返回一个空的不可变的xxx
- Collections.singletonXXX():返回一个只包含指定对象的,不可变的集合对象
- unmodifiableXXX():返回指定集合对象的不可变视图
Optional容器类
一个可以为null的容器对象,如果值存在isPresent方法会返回true,调用get方法会返回该对象
package collection;
import java.util.Optional;
public class OptionalDemo {
public static void main(String[] args){
Optional<String> optional1 = Optional.of("family");//创建一个非null的Optional
Optional<String> optional2 = Optional.ofNullable("house");//为指定的值创建一个Optional,如果指定值为空返回一个空的Optional
System.out.println(optional1.isPresent());//如果值存在返回true,否则返回false
System.out.println(optional2.get());//如果值存在返回该值,否则抛出NoSuchElementException
optional1.ifPresent((value)->System.out.println(value.toUpperCase()));//如果Optional实例有值则为其调用consumer,否则不作处理
System.out.println(optional2.orElse("无值"));//如果有值就返回,否则返回指定的其他值
optional2.orElseGet(()->"default");//与orElse方法相似,区别在于该方法可以接受Supplier接口的实现用来生成默认值
Optional<String> optional3 = Optional.ofNullable(null);
try {
optional3.orElseThrow(Exception::new);//如果有值将其返回,否则抛出supplier异常
} catch (Exception e) {
e.printStackTrace();
}
Optional<String> optional4 = optional1.map((value)->value.toUpperCase());//如果有值,执行mapping函数返回值,如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional
System.out.println(optional4.orElse("没有值"));
Optional<String> optional5 = optional1.flatMap((value)->Optional.of(value.toUpperCase()));
System.out.println(optional5.orElse("没有值"));//与map类似,但该方法不会将返回值封装成Optional封装
optional4 = optional4.filter((value)->value.length()>3);//如果有值并且满足断言条件就返回该值的Optional,否则返回空的Optional
System.out.println(optional4.orElse("该值长度不大于三"));
}
}
堆和栈
package collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class QueueAndStackDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();//队列,先进先出
Deque<String> deque = new LinkedList<>();//双端队列
Stack<String> stack = new Stack<>();
queue.add("小白");
queue.add("小黑");
queue.add("小花");
queue.add("小红");
System.out.println(queue.size());
System.out.println(queue.peek());//取值但不删除
System.out.println(queue.poll());//取值且移除
queue.remove();//删除
}
}
对象的一对多关系
import java.util.HashSet;
public class Teacher {
private String name;
private int age;
private HashSet<Student> students = new HashSet<>();
public Teacher() {
}
public Teacher(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;
}
public HashSet<Student> getStudents() {
return students;
}
public void setStudents(HashSet<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Student {
private String name;
private int age;
private Teacher teacher;
public Student() {
}
public Student(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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", teacher=" + teacher +
'}';
}
}
package collection;
public class HasManyDemo {
public static void main(String[] args) {
Teacher teacher = new Teacher("邢老师",8);
Student student1 = new Student("小a",10);
Student student2 = new Student("小b",12);
Student student3 = new Student("小c",4);
Student student4 = new Student("小d",35);
teacher.getStudents().add(student1);
teacher.getStudents().add(student2);
teacher.getStudents().add(student3);
teacher.getStudents().add(student4);
student1.setTeacher(teacher);
student2.setTeacher(teacher);
student3.setTeacher(teacher);
student4.setTeacher(teacher);
print(teacher);
}
private static void print(Teacher t1){
System.out.println(t1.getName());
System.out.println(t1.getAge());
for(Student s:t1.getStudents()){
System.out.println(s);
}
}
}