前言
之前我们学习了使用数组来保存多个对象,但数组长度不可变化,如果需要保存数量变化的数据,数组就有点无能为力了,所以我们需要使用集合。
List 、Set、 Map
List:有序的集合,可以重复
Set:无序的集合,不可重复
Map: key-value
由图中可以看出,List和Set的父接口都是Collection
集合类和数组的不一样:
数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合里只能存放引用变量。
集合重写了equals()和hashcode()方法:
方法:
测试Collection接口的常用方法:
//测试collection接口的常用方法
public class Demo1 {
/**
* 泛型是jdk5.0才出现的
* Collection<String> coll=new ArrayList<String>();
* jdk1.7之后
* Collection<String> coll=new ArrayList<>();
* @param args
*/
public static void main(String[] args) {
//Collection<Integer> coll1=new ArrayList<>();
//coll1.add(1); //自动封箱
Collection<String> coll=new ArrayList<String>();
//按顺序添加
System.out.println(coll.isEmpty()); //true
coll.add("hello");
coll.add("abc");
coll.add("123");
// boolean isEmpty(): 返回集合是否为空 当集合长度为0 时返回 true ,否则返回 false
boolean b=coll.isEmpty();
System.out.println(b); //false
// int size(): 该方法返回集合里元素的个数。
System.out.println(coll.size());
// boolean contains(Object o): 返回集合里是否包含指定元素
System.out.println(coll.contains("hello")); //true
// boolean remove(Object 0): 除集合中的指定元素。
coll.remove("hello");
System.out.println(coll.size()); //2
coll.clear();
System.out.println(coll.isEmpty()); //true
}
}
遍历集合元素的三种方式:
Collection<String> coll=new ArrayList<String>();
System.out.println(coll.isEmpty()); //true
coll.add("hello");
coll.add("abc");
coll.add("123");
//第一种方法:增强for循环
for(String str:coll) {
System.out.println(str);
}
System.out.println("==========");
//第二种方法:迭代器
Iterator<String> it=coll.iterator();
//当前容器中是否有元素
while(it.hasNext()) {
String str=it.next();
System.out.println(str);
}
System.out.println("==========");
//第三种:lambda
coll.forEach(a->System.out.println(a));
和集合相关的方法:
//和集合相关的方法
public static void main(String[] args) {
// TODO Auto-generated method stub
Collection<String> c1=new ArrayList<>();
c1.add("c");
c1.add("c++");
c1.add("java");
Collection<String> c2=new ArrayList<>();
c2.add("jsp");
c2.add("php");
c2.add("java");//可以添加进去,因为List有序
// boolean containsAll(Collection c):集合里是否包含集合c里的所有元素。
boolean b=c1.containsAll(c2);
System.out.println(b);
// boolean addAll(Collection c) 该方法把集合c 里的所有元素添加到指定集合里。
boolean b2=c1.addAll(c2);
c1.forEach(str->System.out.println(str));
//c1.removeAll(c2);
c1.forEach(str->System.out.println(str));
}
List
默认初始容量:10
ArrayList底层的实现是线性表,实际是数组:
transient关键字修饰的变量不会被序列化。
下面程序示范了 List 集合的常规用法。
List<String> list=new ArrayList<>();
list.add("hello");
list.add(0,"abc");
list.forEach(str->System.out.println(str));
list.set(1, "111");
list.forEach(str->System.out.println(str));
list.remove(0);
list.forEach(str->System.out.println(str));
System.out.println("============");
//遍历
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
集合和数组的相互转换及排序:
//集合转成数组
public static void test1() {
List<String> list=new ArrayList<>();
list.add("hello");
list.add(0,"abc");
List<Integer> list2=new ArrayList<>();
list2.add(1);
list2.add(2);
Object[] obj=list2.toArray();
int sum=0;
for(Object object:obj) {
sum+=(Integer)object;
}
//经常用的方法
Integer[] li=list2.toArray(new Integer[list2.size()]);
for(Integer i:li) {
sum+=i;
}
//对数组排序
//默认升序
Arrays.sort(li);
System.out.println(Arrays.toString(li));
String[] strs=new String[list.size()];
Arrays.sort(list.toArray(strs));
System.out.println(Arrays.toString(strs));
}
static Comparator<String> com=new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
};
//数组转成集合 asList()
//给集合排序,使用的是 Collections的sort方法 //底层还是Arrays.sort()
public static void test2() {
String[] str= {"hello","abc","bde"};
//数组转成集合
List<String> list=Arrays.asList(str);
//集合的排序
Collections.sort(list); //底层还是Arrays.sort()
System.out.println(list);
//自定义排序规则:按长度排序
//Collections.sort(list,com);
//匿名表达式的形式
Collections.sort(list,(o1,o2)->o1.length()-o2.length());
System.out.println(list);
}
练习:按照学生成绩排序
1.定义Student类
两个变量:name,score
方法:
2.定义集合,3个student对象,添加到集合
3.使用sort方法排序,排序是指定排序规则
4.输出排序后的集合
//方法一:
public class Student {
private String name;
private int score;
public Student(){
}
public Student(String name, int score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", score=" + score + "]";
}
}
//方法二:
public class Student2 implements Comparable<Student2>{
private String name;
private int score;
public Student2(){
}
public Student2(String name, int score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", score=" + score + "]";
}
@Override
public int compareTo(Student2 o) {
return this.score-o.score;
}
}
List<Student> stuList = new ArrayList<>();
stuList.add(new Student("admin",60));
stuList.add(new Student("zhangsan",90));
stuList.add(new Student("lisi",80));
Collections.sort(stuList,
(stu1,stu2)->stu1.getScore()-stu2.getScore());
System.out.println(stuList);
System.out.println("===========================");
List<Student2> stuList2 = new ArrayList<>();
stuList2.add(new Student2("admin",60));
stuList2.add(new Student2("zhangsan",90));
stuList2.add(new Student2("lisi",80));
Collections.sort(stuList2);
System.out.println(stuList2);
练习:
实现ArrayList
//MyArray (String)
class MyArray{
//int size;//表示集合的元素个数
//String[] strArray;
private int size;
private String[] strArray;
//1.定义无参的构造方法 默认给数组10个
public MyArray(){
strArray = new String[10];
}
//2.定义一个带参的构造方法
public MyArray(int length){
strArray = new String[length];
}
/*3.add(String str);
判断元素的个数和数组的长度之间的关系 size>=strArray 扩容
添加元素
size++;*/
public void add(String str){
if(size>=strArray.length){
grow();
}
strArray[size]=str;
size++;
}
/*4.定义grow方法
规则:数组长度的一半(如果数组长度为1 ,扩容应该是+1)*/
private void grow(){
if(strArray.length<=1){
strArray = Arrays.copyOf(strArray, strArray.length+1);
}else{
strArray = Arrays.copyOf(strArray, strArray.length+strArray.length/2);
}
}
/*5.add(int index,String str)
判断元素的个数和数组的长度之间的关系 size>=strArray 扩容
当前的数组元素后移
插入元素
size++;*/
public void add(int index,String str){
if(size>strArray.length){
grow();
}
//{1,2,3} index = 1 3 - 1
//{1,1,2,3}
System.arraycopy(strArray, index, strArray, index+1, size-index);
strArray[index] = str;
size++;
}
/*6.remove(int index)
当前的数组元素前移
size--;*/
public void remove(int index){
//{1,2,3} index = 1 3 1-1
//{1,3}
System.arraycopy(strArray, index+1, strArray, index, size-index-1);
strArray[--size]=null;
}
//7.set(int index,String str):修改
public void set(int index,String str){
strArray[index] = str;
}
/*8.get(int index):获取元素*/
public String get(int index){
return strArray[index];
}
/*9.size():返回集合中的元素个数*/
public int size(){
return size;
}
}
public class Demo {
public static void main(String[] args) {
MyArray arr = new MyArray();
arr.add("hello");
arr.add("abc");
arr.add("admin");
arr.remove(1);
arr.set(1, "admin1111");
arr.add(1,"222");
for(int i = 0;i<arr.size();i++){
System.out.println(arr.get(i));
}
}
}
泛型
什么是泛型?
java泛型设计原则:编译不出错,运行时不会出现ClassCastException
泛型基础
1.泛型类:
在类上定义的泛型,在方法中也可以使用
class TestObj<T>{
private T obj;
private int age;
private String name;
......
}
测试代码:(通过泛型定义变量的类型,比如老师工资和学生学号)
public class TestObj<T> {
private T data;
private String name;
public TestObj() {
}
public TestObj(T data, String name) {
super();
this.data = data;
this.name = name;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "TestObj [data=" + data + ", name=" + name + "]";
}
}
TestObj<String> stu=new TestObj<>();
stu.setName("张三");
stu.setData("201716122387");
System.out.println(stu);
TestObj<Float> emp=new TestObj<>();
emp.setData(10000.231f);
emp.setName("老师");
System.out.println(emp);
结果:
TestObj [data=201716122387, name=张三]
TestObj [data=10000.231, name=老师]
2.泛型方法
<T> void f(){}
方法返回类型前面有<>才是泛型方法
泛型方法所在的类不一定是泛型类
//输出任意数据类型的数组元素
public static <T> void showData(T[] arr) {
for(T t:arr) {
System.out.println(t);
}
}
public static void test3() {
Integer[] num= {1,2,3};
//必须是引用类型
showData(num);
}
结果:
1
2
3
3.泛型类派生出的子类:
-
子类明确泛型类的类型参数变量,该子类就是一个普通类
-
子类不明确泛型类的类型参数变量,该子类是一个泛型类
以接口为例:泛型接口
泛型接口:
interface Demo<T>{}
interface Inter<T>{
void show(T t);
}
class Im implements Inter<String>{
@Override
public void show(String t) {
System.out.println(t);
}
}
class Im2<T> implements Inter<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
使用规则:
public class Demo3 {
public static void main(String[] args) {
Inter<String> i2= new Im();
i2.show("hello");
Inter<Integer> i3= new Im2<>();
i3.show(1);
}
}
4.泛型的使用-通配符 ? 表示任意数据类型
public static void show(List<Object> list){
for(Object obj : list){
System.out.println(obj);
}
}
List<Object> 和 List<String>不存在继承关系
5.设置通配符的上限List<? extends Animal> list
6.设置通配符的下限List<? super Number> list
public class Demo4 {
public static void show(List<?> list){
for(Object obj : list){
System.out.println(obj);
}
}
public static void test1(List<? extends Number> list){
for(Number obj : list){
System.out.println(obj);
}
}
public static void test2(List<? super Number> list){
for(Object obj : list){
System.out.println(obj);
}
}
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
//List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
test2(list);
/*List<String> list = new ArrayList<>();
list.add("abc");
list.add("hello");
list.add("123");
show(list);*/
}
}
通配符的错误使用:
class A<?>{}
public <?> void t(List<?> list){}
List<?> list = new ArrayList<?>(); //错在ArrayList<?>()
Stream
Stream:和io没有任何关系
流式思想:像生产流水线一样,一个操作接一个操作。
使用流操作集合更高效
2.stream的特性
1.stream不存储数据
2.stream不改变源数据
3.stream的延迟执行特性
3.使用Stream流的步骤:数据源→转换成流→操作1→操作2→……
4.数据源(source):可以是集合、数组等。
5.获取流:
// List
list.stream();
// 数组
Stream.of();
6.常用方法
Stream只能被使用一次,如果还想使用,必须重新获取流。
如果不重新获取,会得到异常:stream has already been operated upon or closed
1.filter 方法用于通过设置的条件过滤出元素
2.map 方法用于映射每个元素到对应的结果
3.limit 方法用于获取指定数量的流
4.跳过前n个stream.skip
5.流的连接Stream.concat(stream1,stream2)
6.回数量count
7.可以把流转换为 List 集合类型:collect(Collectors.toList());
8.求和的方法reduce(0, (x, y) -> x + y)
9.去重distinct()
10.排序:sorted()
返回的是流,就可以用foreach()
public class Demo5 {
public static void test2() {
Stream<Integer> stream=Stream.of(1,2,3,4,5,6,7);
Stream<Integer> numStream=stream.filter(e->e%2!=0);//有延迟
//返回的是流,就可以用foreach
numStream.forEach(e->{
System.out.println("=======");
System.out.println(e);
});
System.out.println("filter");
stream=Stream.of(1,2,3,4);
stream.map(e->e*e).forEach(e->System.out.println(e));
stream.limit(3).forEach(e->System.out.println(e));
System.out.println("=======");
//跳过
stream.skip(3).forEach(e->System.out.println(e));
}
public static void test() {
List<String> listStr=new ArrayList<>();
listStr.add("hi");
listStr.add("heelo");
Integer[] num= {1,2,3,4};
Stream<String> s=listStr.stream();
Stream<Integer> i=Stream.of(num);
Stream.concat(s, i).forEach(e->System.out.println(e));
//报异常stream has already been operated upon or closed
i.forEach(e->System.out.println(e));
i.forEach(e->System.out.println(e));
}
public static void test3() {
Stream<Integer> stream=Stream.of(1,2,3,4,5,6,7);
System.out.println(stream.count());
List<Integer> i=stream.collect(Collectors.toList());
System.out.println(i);
//0相当于sum初始值
System.out.println(stream.reduce(0,(x,y)->x+y));
stream=Stream.of(1,2,1,3);
stream.distinct().forEach(e->System.out.println(e));
stream=Stream.of(2,5,3);
stream.sorted().forEach(e->System.out.println(e));
//从大到小排
stream.sorted((x,y)->y-x).forEach(e->System.out.println(e));
}
}
练习:
1.有如下整数1,-2,-3,4,-5,使用Stream取元素绝对值并打印
stream.map(e->Math.abs(e)).forEach(System.out::print);
2.给定一个数字列表,如何返回一个由每个数的平方构成的列表
给定【1,2,3,4,5】, 应该返回【1,4,9,16,25】。
stream.map(e-> e*e).forEach(System.out::print);
3.有如下7个元素黄药师,冯蘅,郭靖,黄蓉,郭芙,郭襄,郭破虏,
使用Stream将以郭字开头的元素存入新集合
stream.filter(e->e.starWith("郭")).colect(Colectors.toList());
4.已知ArrayList集合中有如下元素{陈玄风、梅超风、陆乘风、曲灵风、
武眠风、冯默风、罗玉风},
使用Stream
1、取出前2个元素并在控制台打印输出。
stream.limit(3).forEach(System.out::print);
2、取出后2个元素并在控制台打印输出。
stream.skip(list.size()-2).forEach(System.out::print);
5.怎样用 map 和 reduce 方法数一数流中有多少个Employee
List emps = Arrays.asList(
new Employee(102, “李四”, 59),
new Employee(101, “张三”, 18),
new Employee(103, “王五”, 28),
new Employee(104, “赵六”, 8),
new Employee(105, “田七”, 38)
);
stream.map(e->1).reduce(0,(x,y)->x+y)
6.找出2011年发生的所有交易, 并按交易额排序(从低到高)
class Transaction {
private int year; //交易时间
private int value; //交易额
方法。。。。
List<Transaction> transactions =Arrays.asList(
new Transaction(2011, 300),
new Transaction(2012, 1000),
new Transaction(2011, 400),
new Transaction(2012, 710),
new Transaction(2011, 200),
new Transaction(2012, 700),
new Transaction(2012, 950)
);
stream.filter(e->e.getYear()==2011).
sorted((t1,t2)->t1.getValue()-t2.getValue()).forEach(....);
集合
List集合的实现类
LinkedList和ArrayList都是线程不安全的
Vector是线程安全的
List<String> list =
new LinkedList<>();
List<String> v = new Vector<>();
练习:
class LinkList{
private int size = 0; // 节点个数
private Node first; // 第一个节点
private Node last; // 最后一个节点
//无参构造方法
public LinkList() {
}
//添加元素
public void add(String str) {
Node node=new Node(null,str,null);
if(size==0) {
this.first=node;
this.last=node;
}else {
this.last.next=node;
node.prev=this.last;
this.last=node;
}
size++;
}
//插入
//插入时先写最后插入
public void add(int index, String str) {
//最后,this.add()
if(index == size){
this.add(str);
return;
}
Node node = new Node(null,str,null);
if(index==0){
//在最前边
node.next = this.first;
this.first.prev = node;
this.first = node;
}else{
Node node1 = this.getNode(index);
node1.prev.next = node;
node.prev = node1.prev;
node.next = node1;
node1.prev = node;
}
size++;
}
// 获取指定位置的节点
Node getNode(int index) {
Node node=this.first;
for(int i=0;i<index;i++) {
node=node.next;
}
return node;
}
//删除
public void remove(int index) {
if(index == 0){
this.first.next.prev = null;
this.first = this.first.next;
}else if(index == size-1){
this.last.prev.next = null;
this.last = this.last.prev;
}else{
Node node = this.getNode(index);
node.prev.next = node.next;
node.next.prev = node.prev;
}
size--;
}
//返回节点的内容
public String get(int index){
return this.getNode(index).data;
}
// 返回元素个数
public int size(){
return size;
}
//利用节点存储数据
private class Node {
Node prev; // 上一个节点
String data; // 元素
Node next; // 下一个节点
public Node(Node prev, String data, Node next) {
super();
this.prev = prev;
this.data = data;
this.next = next;
}
}
}
public class DemoLinkedList {
public static void main(String[] args) {
LinkList linkList=new LinkList();
linkList.add(0,"hello");
System.out.println(linkList.size());
linkList.add(1,"success");
linkList.add(2,"su");
System.out.println(linkList.get(0));
System.out.println(linkList.get(1));
System.out.println(linkList.get(2));
linkList.remove(2);
System.out.println(linkList.get(0));
System.out.println(linkList.get(1));
}
}
运行结果:
1
hello
success
su
hello
success
Set集合
不重复:先判断hashCode(); 相同,还要判断equals()方法,如果返回true表示一个对象,否则是不同对象
HashSet和TreeSet
HashSet基于HashMap的实现
HashSet的元素是map中的key,value值对应的是一个常量
HashSet:
public class Demo7 {
public static void test(){
Set<String> set =
new HashSet<>();
set.add("张三");
set.add("张三");
set.add("李四");
set.add("王五");
set.remove("王五");
set.forEach(System.out::println);
}
public static void test2(){
Set<Emp> set = new HashSet<>();
set.add(new Emp("张三"));
set.add(new Emp("张三"));
set.add(new Emp("李四"));
set.add(new Emp("王五"));
set.forEach(System.out::println);
//Emp [name=张三]
//Emp [name=李四]
//Emp [name=王五]
}
public static void main(String[] args) {
test2();
}
}
public class Emp {
private String id;
private String name;
public Emp(){
}
public Emp(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
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;
Emp other = (Emp) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Emp [name=" + name + "]";
}
}
TreeSet的元素必须是有排序规则的对象,否则会运行时异常
public static void test(){
TreeSet<String> set = new TreeSet<>();
set.add("abc");
set.add("bed");
set.add("avs");
set.forEach(System.out::println);
}
public static void test2(){
TreeSet<Emp> set = new TreeSet<>(
(emp1,emp2)->
emp1.getName().length()-emp2.getName().length());
set.add(new Emp("admin"));
set.add(new Emp("baf"));
set.add(new Emp("abmin333"));
set.forEach(System.out::println);
}
Map集合