---------------------- android培训、java培训、期待与您交流! ---------------------- ----------------------
集合
集合是由数组过来的,数组不能随着元素的增加,长度增长。数组中可以存储基本的数据类型,也可以装引用类型。
集合只能用于存储对象,集合的长度是可变的,集合可以存储不同类型的对象。
Collection接口的功能描述:
1.添加功能
2.删除功能
3.修改功能
4.获取功能
5.长度功能
Collection接口中方法 :
public class Collection {
public static void main(String[] args) {
//添加一个元素 返回值为boolean
//只会返回true
Collection c1 = new ArrayList();
boolean flag1 =c1.add("hello");
c1.add("world");
System.out.println("c1:"+c1+" "+flag1);
//将指定集合元素 添加到当前集合中
Collection c2 = new ArrayList();
boolean flag2 = c2.addAll(c1);
System.out.println("c2:"+c2+" "+flag2);
//移除此集合中的所有元素
Collection c3 = new ArrayList();
c3.add("hah");
c3.add("heihei");
c3.clear();
System.out.println("c3:"+c3+" ");
//是否包含指定元素 有返回true
System.out.println("c1是否包含hello "+c1.contains("hello"));
//是否包含指定集合中所有元素
Collection c4 = new ArrayList();
c4.addAll(c2);
c4.add("me");
System.out.println("c1是否包含c4所有元素 "+c1.containsAll(c4));
//集合中没有元素 返回true
System.out.println("c3中没有元素: "+c3.isEmpty());
//移除元素
Collection c5 = new ArrayList();
c5.add("hh");
c5.add("bb");
c5.remove("hh");
System.out.println("c5移除hh: "+c5);
//移除此集合中那些也包含在指定集合中的所有元素
//如果此集合内容发生改变 就返回true
//换句话来说 就是 A和B中元素,不相同的在A中留下
Collection c6 = new ArrayList();
c6.add("hh");
c6.add("bb");
Collection c7 = new ArrayList();
c7.add("hh");
c7.add("bb");
c7.add("cc");
boolean flag3 =c6.removeAll(c7);
System.out.println(c6+" "+flag3);
//移除此集合中未包含在指定集合中的所有元素
//如果此集合改动 返回true
//换句话说 就是在A和B中元素,相同的在A中留下
Collection c8 = new ArrayList();
c8.add("hh");
c8.add("bb");
c8.add("d");
Collection c9 = new ArrayList();
c9.add("hh");
c9.add("bb");
c9.add("cc");
boolean flag4 = c8.retainAll(c9);
System.out.println("c8:"+c8+" "+flag4);
}
}
//输出结果:
c1:[hello, world] true
c2:[hello, world] true
c3:[]
c1是否包含hello true
c1是否包含c4所有元素 false
c3中没有元素: true
c5移除hh: [bb]
c6:[] true
c8:[hh, bb] true
/*
创建集合存储5个学生对象,并把学生对象遍历
*/
public class student {
private String name;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public student(String name, String sex) {
this.name = name;
this.sex = sex;
}
public student(){
}
@Override
public String toString() {
return "[姓名:"+name+"sex:"+sex+"]";
}
}
//这个使用了泛型,如果不懂,去看我的泛型文章吧。。。
public class Test{
public static void main(String[] args){
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三","man"));
list.add(new Student("李四","man"));
list.add(new Student("王五","man"));
list.add(new Student("张飞","women"));
list.add(new Student("二狗子","man"));
for(Student i:list){
System.out.println(i.toString());
}
}
}
//输出结果:
[姓名:张三sex:man]
[姓名:李四sex:man]
[姓名:王五sex:man]
[姓名:张飞sex:women]
[姓名:二狗子sex:man]
迭代器: 是遍历集合的一种方式。迭代器是依赖于集合存在的。
为什么设计为接口?
因为各个集合的存储结构不同,所以存储的方式和遍历的方式是不同的,
而每个集合都应该具备,而每个集合类都应该具备获取功能,并且最好有
辅助判断功能,在获取前判断,不容易出错.
原理:
查看源码得知
Collection接口继承了Interable接口
Interable接口中有一个抽象的方法:Interator<T> iterator()
又找到 Interator为接口。
我们去这个接口中看看都有什么方法?
大概是:hasNext() next 方法
这样我们整理一下:
public interface Interator{
hasNext();
next();
}
public interface Interable {
Interator iterator();
}
public interface Collection extends Interable{
}
public interface List extends Collection{
}
public class ArrayList implements List{
//它不是抽象类 所以它要实现 Interator iterator()方法
Interator<T> iterator(){
//返回的是一个实现对象
return new Myiterator();
}
private class iterator implements Interator{
hasNext(){
}
next(){
}
}
}
public class Demo{
public static void main(String[] args){
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三","man"));
list.add(new Student("李四","man"));
list.add(new Student("王五","man"));
Iterator<Student> it = list.iterator();
while(it.hasNext()){
//这里它直接调用了内部类的next()方法
System.out.println(it.next().toString());
}
}
}
//输出结果:
[姓名:张三sex:man]
[姓名:李四sex:man]
[姓名:王五sex:man]
List集合是一个有序的Collection。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。
用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素
与Set不同,列表通常允许重复的元素。
List接口特有方法:
public class list {
public static void main(String[] args) {
//在指定位置添加元素
List list = new ArrayList<>();
list.add("ha");
list.add("heihei");
list.add(2,"hehe");
System.out.println(list);
//获取指定位置元素
System.out.println((String)list.get(1));
//列表迭代器
ListIterator lt = list.listIterator();
while(lt.hasNext()){
System.out.println((String)lt.next());
}
//逆向遍历
while(lt.hasPrevious()){
System.out.println(lt.previous());
}
//删除功能
list.remove(1);
System.out.println(list);
//修改功能
list.set(0, "heihei");
System.out.println(list);
}
}
//输出结果:
[ha, heihei, hehe]
heihei
ha
heihei
hehe
hehe
heihei
dd
ha
[ha, hehe]
[heihei, hehe]
迭代器并发异常:
public class listerr {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
//并发修改错误
//Iterator是依赖于集合的,而集合在修改时,it并不知道
//Iterator it = list.iterator();
//使用list特有迭代器解决
ListIterator lt = list.listIterator();
while(lt.hasNext()){
if(lt.next().equals("world")){
lt.add("b");
}
}
System.out.println(list);
}
}
总结一下:
List 子类特点:
实现类 底层数据结构 安全性 查询 增删 效率
ArrayList 数组 yes yes no yes
LinkedList 链表 yes no yes yes
Vector 数组 no yes no no
ArrayList练习:
/**
*
* 将集合中字符串重复的移除
*
*/
public class Arraylist1 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("haha");
list.add("world");
list.add("world");
list.add("hello");
list.add("java");
List newList = new ArrayList();
Iterator it = list.iterator();
while(it.hasNext()){
String str = (String) it.next();
//注意这个方法
if(!newList.contains(str)){
newList.add(str);
}
}
System.out.println(newList);
}
}
/**
*
* 将集合对象属性相同的去掉
*
*/
public class Arraylist2 {
public static void main(String[] args) {
List<Student> list= new ArrayList<Student>();
list.add(new Student("小明","男"));
list.add(new Student("小红","女"));
list.add(new Student("小明","男"));
List<Student> list2= new ArrayList<Student>();
Iterator it = list.iterator();
while(it.hasNext()){
Student stu = (Student) it.next();
//其实它是判断用的对象的equals方法
//源码 (o.equals(elementData[i]))
//重写对象equals()方法就可以比较两个对象的内容了
if(!list2.contains(stu)){
list2.add(stu);
}
}
System.out.println(list2);
}
}
/**
*从键盘读取数字 输入0结束 输出最大值
*/
public class ArrayListTest2 {
public static void main(String[] args) {
List<Integer> list = new ArrayList();
while(true){
System.out.println("请输入数字");
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
if(!(i==0)){
list.add(i);
}else{
break;
}
}
Integer[] integer = new Integer[list.size()];
list.toArray(integer);
Arrays.sort(integer);
System.out.println(integer[integer.length-1]);
}
}
//输出结果:
请输入数字
20
请输入数字
30
请输入数字
0
30
/**
*
* 请用LinkedList模拟栈数据结构的集合,并测试
*
*/
public class MyStack{
private LinkedList link;
public MyStack(){
this.link = new LinkedList();
}
public boolean add(Object o){
return link.add(o);
}
public Object get(){
//返回 并弹栈
return link.removeFirst();
//它只是返回第一个 并没有弹栈 如果调用的话 一直返回的是第一个
// return link.getFirst();
}
public boolean isEmpty(){
return link.isEmpty();
}
}
public class MyStackTest {
public static void main(String[] args) {
MyStack ms = new MyStack();
ms.add("hah");
ms.add("heihei");
System.out.println(ms.get());
//每次get都会弹栈
System.out.println(ms.get());
System.out.println("没有东西了?"+ms.isEmpty());
}
}
Set集合
HashSet:不保证set的迭代顺序,特别是它不保证该顺序不变。
底层数据结构是哈希表(元素是链表的数组)
哈希表依赖于哈希值的存储
底层依赖于两个方法:
int hashCode();
boolean equals(Object o);
源码分析得知新添加的元素要和前面所有元素比较hash值,如果hash值相同,再比较equals.
如果继承Object hash值肯定不相同,而String重写了hashcode方法,hash值相同。
我们为了减少它的排查,我们重写hashcode方法,用变量去影响它的hash值,这样只有变量的hash
值相等时,才有可能是一个元素。
分析: 如果我们的Person类继承于Object,那么它的hash值肯定都是不同的
public class HashSetDemo{
public static void main(String[] args) {
Set<Student> s = new HashSet();
s.add(new Student("zhangsan","nan"));
s.add(new Student("zhangsan","nan"));
s.add(new Student("lisi","nv"));
s.add(new Student("zhangsan","nan"));
s.add(new Student("zhangsan","nan"));
for(Student d :s){
System.out.println(d);
}
}
//输出结果:
[姓名:zhangsansex:nan]
[姓名:lisisex:nv]
[姓名:zhangsansex:nan]
[姓名:zhangsansex:nan]
[姓名:zhangsansex:nan]
HashSet是保证唯一性的,但从次看来并没有,也就是说明了,它走对象的hashcode时,判断了hash值
不同,所以直接添加。
如果我们传的String对象的话,会调用String重写hashcode方法,这时相等的字符串会出现相同的hash
值。所有HashSet的第一个条件就进去了,然后比较内容,如果相同,就不会添加。
从这个角度来想,如果我们想让自定义对象保证唯一性,必须重写hashcode方法。
那么我们可以将hash值与变量关联起来。
这样写: this.name.hashCode()+this.sex.hashCode();
如果两个String都相等说明可能是同一个元素
但是还会出现这样的情况: 15 20 20 15
我们可以看出它们根本不是相同元素,这时hash值还是相等,我们为了提高效率,在一个属性后面
*一个值。
这样优化后,只有它们的属性hash值相等时,才可能是同一个元素。
如果不相同,直接添加。
/**
* 存储自定义对象 保证元素唯一性
* 如果成员变量值 相同,则为同一个元素。
*
*/
public class Student {
private String name;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
public Student(){
}
@Override
public String toString() {
return "[姓名:"+name+"sex:"+sex+"]";
}
@Override
public int hashCode() {
//这里修改了hash值 只有两个属性的hash值 加起来 相等时,才不会添加到集合中
return this.name.hashCode()+this.sex.hashCode()*15;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
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;
}
}
public class HashSetTest {
public static void main(String[] args) {
System.out.println(str.hashCode()+" " +str2.hashCode());
Set<Student> s = new HashSet();
s.add(new Student("zhangsan","nan"));
s.add(new Student("zhangsan","nan"));
s.add(new Student("lisi","nv"));
s.add(new Student("zhangsan","nan"));
s.add(new Student("zhangsan","nan"));
//两个hash值相等时,才判断为同一个元素
s.add(new Student("lisi","nan"));
for(Student d :s){
System.out.println(d);
}
}
}
//输出结果:
[姓名:lisisex:nan]
[姓名:lisisex:nv]
[姓名:zhangsansex:nan]
LinkedHashSet:
元素有序唯一
由链表保证元素有序
由哈希表保证元素唯一
public class LinkedSetDemo {
public static void main(String[] args) {
Set<String> s = new LinkedHashSet<>();
s.add("hello");
s.add("java");
s.add("world");
for(String d: s){
System.out.println(d);
}
}
}
//输出结果:
hello
java
world
TreeSet:
使用元素的自然排序对元素进行排序
或者根据创建时提供的Conparator进行排序
具体取决于使用的构造方法
排序: 内部使用二叉树排序(红黑树属于一种自平衡二叉树)
源码: 首先创建根节点,如果第二个元素比根节点小 就放左边,大就放右边。相等不放。
读取顺序 左中右 数据结构来说叫:中序遍历
实现排序有两种:
自然排序
比较器排序
/**
* 自然 排序 先按年龄从小到大 在按姓名排
*/
public class Student implements Comparator<Student>{
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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;
}
@Override
public int compareTo(Student o) {
//如果我们想从小到大排列的话 就用根减去比较值
//如果从大到小的话 用比较值-根
int num = this.age - o.age;
int num2 = num==0?this.name.compareTo(o.name):num;
return num2;
}
}
public class Test {
public static void main(String[] args) {
Set<Student> s = new TreeSet<>();
s.add(new Student("dahjk","nan",5));
s.add(new Student("dahk","nv",2));
for(Student d:s){
System.out.println(d);
}
}
}
//输出结果:
[dahksex:nvage:2]
[dahjksex:nanage:5]
/**
* 比较器排序
*/
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
int num = o1.getAge()-o2.getAge();
int num2 = num==0?o1.getName().compareTo(o2.getName()):num;
return num2;
}
}
public class Test {
public static void main(String[] args) {
Set<Student> s = new TreeSet<>(new MyComparator());
s.add(new Student("dahjk","nan",5));
s.add(new Student("dahk","nv",2));
for(Student d:s){
System.out.println(d);
}
}
}
//输出结果:
[dahksex:nvage:2]
[dahjksex:nanage:5]
一种是类直接实现接口,另一种是自己写一个类继承比较器。
小练习:
/**
* 获取 10个 1-20的的随机数 ,不能重复
*
*/
public class Demo {
public static void main(String[] args) {
Set<Integer> s = new TreeSet<>();
while (s.size()!=10) {
int num = (int) (Math.random()*20+1);
s.add(num);
}
for(Integer a:s){
System.out.println(a);
}
}
}
//输出结果:
6
7
9
11
12
16
17
18
19
20