1.权限修饰符
权限修饰符 | 当前类 | 同一个包下面的其他类 | 其他包下面的子类 | 其他包其他类 |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默认的 | 可以 | 可以 | 不可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
2.集合
2.1为啥要有集合
集合和数组都是可以存数据的
真实开发的时候用的是集合而不是数组
为啥?
1.数组的容量是固定的。
2.数组封装的方法比较少。
Java给咱们封装了集合类库,只需要实例化出来一个对象,使用对象调用方法即可完成数据相关的操作
2.2集合结构
Interface Collection<E> Java 中集合的总接口
---|List<E> Colletion的子接口,特征: 存放数据是有序的,可以重复
---|---| ArrayList<E> List的实现类,里面写好了List接口所有的抽象方法都已经实现好了,功能写好 底层是数组
---|---|LinkedList<E> List的实现类, 里面有自己独有的方法 底层是链表
---|Set<E> Collection的子接口, 特征: 存放数据是无序的,不可重复
--|--| HashSet<E> Set的实现类 底层是Hash算法
--|--| TreeSet<E> Set的实现类 底层是二叉树
2.3Collection接口【开发不用】
Collection下面的方法:
增:
boolean add(E e); 添加数据的
boolean addAll(Collection<? extends E> c); 将一个集合添加到另外的一个集合中
删:
boolean remove(Object obj); 删除指定的元素
boolean removeAll(Collection<?> c)删除指定集合中包含的所有此集合的元素(可选操作)
void clear(); 清空集合中所有的元素
查:
int size(); 集合中元素的个数
Object[] toArray();将集合转为数组
boolean contains(Object obj);在集合中是否包含一个obj元素
boolean containsAll(Collection<? extends E> c); 判断一个集合是否是另外一个结合的子集合
boolean isEmpty(); 判断一个集合是否为空
import java.util.ArrayList;
import java.util.Collection;
public class Demo1 {
public static void main(String[] args) {
//父类的引用指向了子类的对象,意味着 对象只能调用父类的方法
//对象不能调用子类独有的方法 只能调用重写的方法
//Collection 所有的抽象方法都被ArrayList 重写了
Collection<String> collection = new ArrayList<String>();//容器 只能放String类型的数据
collection.add("红旗渠");
collection.add("散花");
collection.add("黄鹤楼");
System.out.println(collection);//[红旗渠, 散花, 黄鹤楼]
Collection<String> collection1 = new ArrayList<String>();//容器 只能放String类型的数据
collection1.add("舍得");
collection1.add("茅台");
collection1.add("牛栏山");
System.out.println(collection1);
collection.addAll(collection1);//将collection1这个集合添加到collection中
System.out.println(collection);//[红旗渠, 散花, 黄鹤楼, 舍得, 茅台, 牛栏山]
// Collection<Integer> collection2 = new ArrayList<Integer>();//容器 只能放String类型的数据
// collection2.add(1);
// collection2.add(2);
// collection2.add(3);
//
// collection.addAll(collection2);
System.out.println(collection.remove("舍得"));//删除指定的数据
System.out.println(collection);//删除之后的结果
//[红旗渠, 散花, 黄鹤楼, 茅台, 牛栏山]
//System.out.println(collection.remove("舍得"));
Collection<String> collection3 = new ArrayList<String>();//容器 只能放String类型的数据
collection3.add("舍得");
collection3.add("茅台");
collection3.add("牛栏山");
collection3.add("红花郎");
System.out.println(collection3);//[舍得, 茅台, 牛栏山, 红花郎]
collection.removeAll(collection3);//删除指定集合中包含的所有此集合的元素
//在collection 删除collection3 中包含的元素
System.out.println(collection);//[红旗渠, 散花, 黄鹤楼]
collection.clear();
System.out.println(collection);//[]
}
}
2.4遍历集合中的数据
3种
1.使用for循环
2.使用增强for循环【重点】
3.使用迭代器【重点】
2.4.1使用for循环遍历集合数据
import java.util.ArrayList;
import java.util.Collection;
public class Demo2 {
public static void main(String[] args) {
Collection<Character> list = new ArrayList<Character>();
list.add('a');
list.add('b');
list.add('c');
System.out.println(list);//[a, b, c]
//for循环遍历
//将集合转为数组 然后再遍历
Object[] objs = list.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
}
}
2.4.2使用增强for循环
语法格式:
for (集合或者数组的元素的数据类型 临时变量名字 : 集合或者数组) {
}
import java.util.ArrayList;
import java.util.Collection;
public class Demo3 {
public static void main(String[] args) {
Collection<String> liStrings = new ArrayList<String>();
liStrings.add("张三");
liStrings.add("狗蛋");
liStrings.add("老邢");
System.out.println(liStrings);
//使用增强for循环遍历数据
for (String s : liStrings) {
System.out.println(s);
}
}
}
2.4.3使用迭代器【重点】
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo4 {
public static void main(String[] args) {
Collection<String> liStrings = new ArrayList<String>();
liStrings.add("张三");
liStrings.add("狗蛋");
liStrings.add("老邢");
//使用迭代器进行遍历
// Iterator<E> iterator() 是Collection 方法
// 返回此集合中的元素的迭代器。
//将集合变成了一个迭代器对象 这个迭代器有没有数据? 有集合中原始的数据的
//1.创建迭代器的对象
Iterator<String> iterator = liStrings.iterator();
//boolean hasNext()如果迭代具有更多元素,则返回 true 。
// ["张三", "狗蛋", "老邢"]
// ||
//游标
// System.out.println(iterator.hasNext());//判断游标后面有没有元素 true
// System.out.println(iterator.next());//返回迭代的下一个元素,并将游标往后挪一位
// System.out.println(iterator.hasNext());
// System.out.println(iterator.next());//狗蛋
// System.out.println(iterator.hasNext());//true
// System.out.println(iterator.next());//老邢
// System.out.println(iterator.hasNext());//false
// System.out.println(iterator.next());
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
2.5List接口
Collection父接口
开发中使用List 而不是Collection 为啥呢?
List下面有自己独有的一些方法,子接口比父接口的功能全
子接口不但重写父接口所有的方法而且加了自己的独有方法
所以开发用List
List接口存储数据的时候 有序的 可以重复
只讲独有的方法
增:
boolean add(int index, E e); 在指定索引下标的位置插入一个数据
boolean addAll(int index, Collection<? extends E> c);
在指定下标为位置 插入另外一个集合
删:
E remove(int index); 通过索引删除数据,返回的是被删除的元素
改:
E set(int index, E e);在指定下标的元素 被别的元素替代,返回值是被替代吗的元素
查:
size()
toArray()
contains()
isEmpty()
E get(int index); 获取指定的下标的元素
int indexOf(Object obj); 通过元素获取指定的下标的
int lastIndexOf(Object obj);获取指定元素的最后一次出现的位置
List<E> subList(int startIndex, int endIndex); 截取集合中的一段
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
System.out.println(list);
list.add("狗蛋");
list.add("张三");
list.add("李四");
list.add("张三");
list.add("李四");
list.add("李四");
list.add("张三");
System.out.println(list);//[狗蛋, 张三, 李四]
list.add(1, "王五");//在指定的下标的位置上面添加一个元素
System.out.println(list);//[狗蛋, 王五, 张三, 李四]
//通过元素删除
System.out.println(list.remove("二蛋"));//false
//通过索引下标删除
System.out.println(list.remove(0));//狗蛋
//
System.out.println(list);//被删除之后的集合元素
//[王五, 张三, 李四]
System.out.println(list.set(1, "王八"));//返回值是被替换的元素
System.out.println(list);//[王五, 王八, 李四]
System.out.println(list.get(2));//李四
System.out.println(list.indexOf("王博"));//-1
System.out.println(list.indexOf("王八"));//1
System.out.println(list.lastIndexOf("张三"));//6
//list = [王五, 王八, 李四, 张三, 李四, 李四, 张三]
System.out.println(list.subList(2, 4));//[李四, 张三]
}
}
2.6List接口数据遍历
1.使用for循环
2.增强for循环
3.迭代器
2.6.1使用for循环
import java.util.ArrayList;
import java.util.List;
public class Demo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("狗蛋");
list.add("张三");
list.add("李四");
list.add("张三");
list.add("李四");
list.add("李四");
list.add("张三");
System.out.println(list);//[狗蛋, 张三, 李四]
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
2.6.2增强for循环
import java.util.ArrayList;
import java.util.List;
public class Demo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("狗蛋");
list.add("张三");
list.add("李四");
list.add("张三");
list.add("李四");
list.add("李四");
list.add("张三");
for (String string : list) {
System.out.println(string);
}
}
}
2.6.3List迭代器
package com.qf.d_list;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo4 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("狗蛋");
list.add("张三");
list.add("李四");
list.add("张三");
list.add("李四");
list.add("李四");
list.add("张三");
// ListIterator<String> listIterator = list.listIterator();
// while (listIterator.hasNext()) {
// System.out.println(listIterator.next());
// }
// //循环结束代码走到这一步 咱们的光标在 最后一个位置
// System.out.println("=========");
// //上一个是否有元素
// while (listIterator.hasPrevious()) {
// //返回上一个元素并且 光标向上一个
// System.out.println(listIterator.previous());
// }
ListIterator<String> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
}
}
List集合中存自定义对象
现在集合容器中存数据的时候,字符串 整型 等
但是也可以存对象,集合中带有泛型 是一个类
import java.util.ArrayList;
import java.util.List;
class Person {
private String name;
private int age;
public Person() {
}
public Person(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;
}
@Override//重写的toString方法 Object类
//将内存地址 转为一个字符串表示的形式 可以看到打印的数据 不再是一个内存地址了
//而是一个字符串了
public String toString() {
return "[name:" + name + ", age:" + age + "]";
}
}
public class Demo1 {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
Person person1 = new Person("狗蛋", 24);
Person person2 = new Person("张三", 14);
Person person3 = new Person("王五", 34);
persons.add(person1);
persons.add(person2);
persons.add(person3);
System.out.println(persons);
//以上在存数据
//可以取数据
// System.out.println(persons.get(0));//获取的是集合中第一个对象
// System.out.println(persons.get(1));
// System.out.println(persons.get(2));
//
// //想只把狗蛋取出来
// //先取出来第一个person对象
// System.out.println(persons.get(0).getName());
for (Person person : persons) {
System.out.println(person.getName() + ":" + person.getAge());
}
}
}
ArrayList源码
RandomAccess :这个接口可以让ArrayList拥有快速随机访问的能力
源码: for循环比迭代器速度更快的
import java.awt.List;
import java.util.ArrayList;
import java.util.ListIterator;
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 1000000; i++) {
list.add("a");
}
//更快点
long start = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
long end = System.currentTimeMillis();//12018毫秒
System.out.println(end - start);
// ListIterator<String> listIterator = list.listIterator();
// while (listIterator.hasNext()) {
// System.out.println(listIterator.next());
// }//15075
}
}
/**
* Default initial capacity.
*/
// DEFAULT_CAPACITY 默认的容量= 10
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
//空的数组 Object[]
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
//m默认的构造方法中空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
//真正存储元素的数组Object[] elementData
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
//最大数组的容量
private int size;
集合默认的可以存10个
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
构造方法是干嘛的? new ArrayList();
elementData 数组 默认是10
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
new ArrayList(4);
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
new ArrayList(list1)
三个构造方法只使用默认的形式
数组里面存10个数据, 但是超过这个数据怎么办? 自动扩容
ArrayList底层是基于动态数组实现的,在什么时候触发扩容? 当调用add、方法的时候 有可能触发
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
//
ensureCapacityInternal(size + 1); // Increments modCount!!
//elementData = {"q", "w"}
//elementData[0] = "q"
//elementData[1] = "w"
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {//最小容量
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//minCapacity 最小容量
// elementData.length 数组长度
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
//扩容方法
private void grow(int minCapacity) {
// overflow-conscious code
//扩容前的数组的长度 10
int oldCapacity = elementData.length;
//扩容后的数组的长度
//int newCapacity = 10 + 10 / 2====> 15
// int newCapacity = oldCapacity + oldCapacity / 2;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//复制 数组
//copyOf(elementData, newCapacity)
//elementData = {1,2,3,4,5,6,7,8,9,10};
// elementData = {1,2,3,4,5,6,7,8,9,10, 0,0,0,0,0}
elementData = Arrays.copyOf(elementData, newCapacity);
}
通过grow是扩容方法 每次扩容 1.5倍
LinkedList【开发不用】
List是LinkedList的父接口
底层是链表
import java.util.LinkedList;
public class Demo1 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
System.out.println(list);
list.addFirst("狗蛋");
System.out.println(list);
list.addLast("网二");
System.out.println(list);
}
}
ArrayList和LinkedList区别
1.ArrayList底层是数组,LinkedList底层是链表
2.ArrayList在随机取数据的时候效率高于LinkedList
3.ArrayList在删除 和插入 的时候效率低于LinkedList
4.ArrayList会自己扩容 需要预留一定空间的
5.LinkedList 是存储数据的节点的信息以及节点信息的内存的指针
Object类
Object是所有类的基类
public String toString()
返回对象的字符串表示形式。 一般来说, toString
方法返回一个“textually代表”这个对象的字符串。 结果应该是一个简明扼要的表达,容易让人阅读。 建议所有子类覆盖此方法。
package com.qf.a_object;
class Person {
String name;
int age;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Demo1 {
public static void main(String[] args) {
Object object = new Object();
Object object2 = new Object();
System.out.println(object);
// 该toString类方法Object返回一个由其中的对象是一个实例,
//该符号字符`的类的名称的字符串@ ”和对象的哈希码的无符号的十六进制表示。 换句话说,这个方法返回一个等于下列值的字符串:
//
// getClass().getName() + '@' + Integer.toHexString(hashCode())
System.out.println(object.toString());//java.lang.Object@15db9742
Person person = new Person();
System.out.println(person);
}
}
public boolean equals(Object obj)
指示一些其他对象是否等于此对象。判断两个对象是否相等
public boolean equals(Object obj) {
return (this == obj);
}
== : b比较的是内存地址
objec1.equals(object2)
为啥 String类下面equals如果内容一样的可以返回true? 当父类的需求满足不了子类的需求
String类重写了Object类的equals
需求: 比较两个对象的内容是否一样【重点】
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {//如果内存地址一样 就返回true
return true;
}
//如果内存地址不一样,但是内容一样咋写
//equals(Object obj) Object obj = student2;//向上转型
//student1.equals(student2)
//obj instanceof Student
if (obj instanceof Student) {// student2 instanceof Student true
Student stu = (Student)obj;//向下转型
//student2===>stu
// student1.name.equals(student2.name) true && student1.age == student2.age
return this.name.equals(stu.name) && this.age == stu.age;
}
return false;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
public class Demo3 {
public static void main(String[] args) {
Student student1 = new Student("土豆", 12);
Student student2 = new Student("土豆", 12);
System.out.println(student1.equals(student2));//因为Object类是Student的父类
//以上是false, 但是真实的开发的时候,关注的是内容 而不是地址
//只要内容一样也得给我true。 咋办?重写eqauls方法
}
}
Employee类 id name
创建两个对象 如果内容一样 使用equals返回的是一个true
class Employee {
int id;
String name;
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
//employee1.equals(employee2)
@Override
public boolean equals(Object obj) {//employee2 赋值 obj 这叫象上转型
//this ===>employee1
//obj =====>employee2
if (this == obj) {//比较内存地址
return true;
}
//如果内存地址不一样的话 接下来判断内容
if (obj instanceof Employee) {//为强转做准备
Employee emp = (Employee) obj;
return this.id == emp.id && this.name.equals(emp.name);
}
return false;
}
}
public class Demo4 {
public static void main(String[] args) {
Employee employee1 = new Employee(1, "老邢");
Employee employee2 = new Employee(1, "老邢");
System.out.println(employee1.equals(employee2));
}
}
public int hashCode()
返回对象的哈希码值。 支持这种方法是为了散列表,如HashMap提供的那样 。
在Object类中,将十六进制的内存地址转为十进制的值,就叫hash值
如果内存地址不一样,那么哈希也是不一样的
hashCode
的总合同是:
如果根据equals(Object)
方法两个对象相等,则在两个对象中的每个对象上调用hashCode
方法必须产生相同的整数结果
class Man {
int id;
String name;
public Man(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Man) {
Man man1 = (Man)obj;
return man1.id == this.id && man1.name.equals(this.name);
}
return false;
}
@Override
public int hashCode() {
return name.hashCode();
}
}
public class Demo6 {
public static void main(String[] args) {
//比较两个对象的而内容是否一致
Man man1 = new Man(2, "蔡徐坤");
Man man2 = new Man(2, "蔡徐坤");
System.out.println(man1.equals(man2));//true
//如果根据`equals(Object)`方法两个对象相等,
//则在两个对象中的每个对象上调用`hashCode`方法必须产生相同的整数结果
System.out.println(man1.hashCode());
System.out.println(man2.hashCode());
//只能重写hashCode方法 不要再使用Object类下面hashCode
}
}
请注意,无论何时覆盖equlas方法,通常需要覆盖hashCode方法,以便维护hashCode方法的通用合同,该方法规定相等的对象必须具有相等的哈希码
class Man {
int id;
String name;
public Man(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
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;
Man other = (Man) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Demo6 {
public static void main(String[] args) {
//比较两个对象的而内容是否一致
Man man1 = new Man(2, "蔡徐坤");
Man man2 = new Man(2, "蔡徐坤");
System.out.println(man1.equals(man2));//true
//如果根据`equals(Object)`方法两个对象相等,
//则在两个对象中的每个对象上调用`hashCode`方法必须产生相同的整数结果
System.out.println(man1.hashCode());
System.out.println(man2.hashCode());
//只能重写hashCode方法 不要再使用Object类下面hashCode
}
}
Set集合
Set集合也是用来存储数据的
Set集合父接口 Collection 接口
Set存储数据的时候的效果是 无序的 不可重复的
Set接口下面有两个 实现类:
HashSet : 底层是hash值进行存储的。如果hash值一样的就无法存到集合中
TreeSet: 底层是二叉树,对存入的数据进行自然排序
2.1HashSet
学习方法
import java.util.HashSet; import java.util.Set; public class Demo1 { public static void main(String[] args) { Set<String> set= new HashSet<String>(); set.add("b"); set.add("c"); set.add("b"); set.add("a"); System.out.println(set);//[a, b, c] Object[] arr = set.toArray(); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } for (String string : set) { System.out.println(string); } } }
set集合中自定义的对象
import java.util.HashSet;
import java.util.Set;
class Person {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
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;
Person other = (Person) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Demo2 {
public static void main(String[] args) {
Set<Person> set = new HashSet<Person>();
Person person1 = new Person(1, "老邢");
Person person2 = new Person(2, "骚磊");
Person person3 = new Person(1, "老邢");
set.add(person1);
set.add(person2);
set.add(person3);
System.out.println(set);
//现在存了三个对象。感觉可以吗?不可以 关注是内容!!!咋干掉?
//重写equlas和hashCode方法
}
}
总结: 如果将对象存入到hashSet中的时候,必须重写当前类的equals和hashCode方法 为了保证对象的内容不重复。
HashSet的底层是HashMap JDK1.8 数组 + 链表 + 红黑树 三个组成
TreeSet
也是Set接口的实现类。可以保证数据唯一型。存储也是无序的。
同时对存入数据会进行自然排序
import java.util.Set;
import java.util.TreeSet;
public class Demo1 {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<Integer>();
set.add(78);
set.add(65);
set.add(21);
set.add(70);
System.out.println(set);//[21, 65, 70, 78]
Set<String> set1 = new TreeSet<String>();
set1.add("b");
set1.add("ba");
set1.add("a");
set1.add("ab");
System.out.println(set1);//[a, ab, b, ba]
for (String string : set1) {
System.out.println(string);
}
}
}
TreeSet存自定义的对象
存对象的时候需要在类的实现Comparable这个接口。该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo
方法被称为其自然比较方法 。
int compareTo(T o)
将此对象与指定的对象进行比较以进行排序。
返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。
person1.compareTo(person2)
如果返回值是负数, 就证明 person1小于 person2 排序 person1 排在person2前面
如果返回值是0,就证明相等,不存了
如果是个正数,就证明大于 person2 排在person1的前面
import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable<Person>{
String name;
int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
//1. this 就是狗蛋 o也是狗蛋
//2. this 潘周丹 o狗蛋 【 潘周丹 狗蛋】
//3. this 旺财 o狗蛋 【潘周丹 狗蛋 旺财 】
//4.this erdsan o狗蛋 【潘周丹 狗蛋 旺财 】
int num = this.age - o.age;
return num;
//2.[潘周丹 狗蛋]
//3.[旺财 狗蛋]
//4.[潘周丹 旺财erdan 狗蛋]
}
}
public class Demo2 {
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>();
set.add(new Person("狗蛋", 24));//这行开始报错
set.add(new Person("潘周丹", 21));
set.add(new Person("旺财", 23));
set.add(new Person("erdsan", 24));
//Exception in thread "main" java.lang.ClassCastException:
//com.qf.c_treeset.Person cannot be cast to java.lang.Comparable
System.out.println(set);
//按照age进行排序
}
}
//按照年龄进行排序
import java.util.Set;
import java.util.TreeSet;
//先按照年龄 如果年龄 再字符串比较
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Student o) {
int num = this.age - o.age;
if (num == 0) {//当 age 相等的时候
int num1 = this.name.compareTo(o.name);
return num1;
}
return num;
}
}
public class Demo3 {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
set.add(new Student("b", 65));
set.add(new Student("d", 35));
set.add(new Student("c", 25));
set.add(new Student("a", 35));
set.add(new Student("浩正", 35));
System.out.println(set);
}
}
通过查阅API我们得知TreeSet集合是基于TreeMap的实现,而TreeMap是基于二叉树(红黑树)结构,也就是说TreeSet集合的底层使用的二叉树(红黑树)结构。
树结构:它也是数据结构中的一种。在计算机领域中树结构指的是倒立的树。
树结构存储的数据,每个数据也需要节点来保存。
而TreeSet集合底层是二叉树的数据结构,什么是二叉树呢?
二叉树:每个节点的下面最多只能有2个子节点。
说明:最多表示一个节点下面可以有两个子节点或者一个子节点或者没有子节点。
在二叉树的根节点左侧的节点称为左子树,在根节点的右侧的节点称为右子树。
既然已经得知TreeSet集合底层是二叉树,那么二叉树是怎样存储数据的呢?是怎样保证存储的数据唯一并有序的呢?
二叉树的存储流程:
当存储一个元素的时候,如果是树的第一个元素,这个元素就作为根节点。
如果不是第一个元素,那么就拿要存储的元素与根节点进行比较大小:
大于根元素:就将要存储的元素放到根节点的右侧,作为右叶子节点。
等于根元素:丢弃。
小于根元素:就将要存储的元素放到根节点的左侧,作为左叶子节点。
总结:二叉树是通过比较大小来保证元素唯一和排序的。
20 10 31 5 13 23 51
关于TreeSet在存储数据的时候,会有一个排序的问题u,。使用一个接口叫Comparable这个接口
咱们还可以使用另外一种方式进行排序。叫比较器的写法
TreeSet(Comparator<? super E> comparator)
构造一个新的,空的树集,根据指定的比较器进行排序。
import java.util.Comparator;
import java.util.TreeSet;
class Emp {
String name;
int age;
public Emp(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Emp [name=" + name + ", age=" + age + "]";
}
}
class MyComparator implements Comparator<Emp> {
@Override
public int compare(Emp o1, Emp o2) {
// TODO Auto-generated method stub
int num1 = o2.age - o1.age;
return num1;
}
}
public class Demo4 {
public static void main(String[] args) {
//借助于有参的构造方法
//TreeSet(Comparator<? super E> comparator)
//构造一个新的,空的树集,根据指定的比较器进行排序。
TreeSet<Emp> emps = new TreeSet<Emp>(new MyComparator());
emps.add(new Emp("狗蛋", 23));
emps.add(new Emp("狗蛋1", 22));
emps.add(new Emp("狗蛋2", 24));
emps.add(new Emp("狗蛋3", 21));
System.out.println(emps);
}
}
匿名内部类
基于抽象类匿名内部类
abstract class Person {
public abstract void eat();
}
//class Man extends Person {
//
// @Override
// public void eat() {
// // TODO Auto-generated method stub
//
// }
//
//}
public class Demo1 {
public static void main(String[] args) {
// Person person = new Person() {
// @Override
// public void eat() {
// // TODO Auto-generated method stub
// System.out.println("吃饭");
// }
// };
// person.eat();
new Person() {
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃汉堡");
}
}.eat();
}
}
真实的开发咋用
一个方法的参数是一个抽象类
abstract class Student {
public abstract void sleep();
}
public class Demo2 {
public static void main(String[] args) {
//test方法参数是抽象类的对象
test(new Student() {
@Override
public void sleep() {
// TODO Auto-generated method stub
System.out.println("睡得老想了");
}
});
}
public static void test (Student stu) {
stu.sleep();
}
}
基于接口的匿名内部类
interface Dog {
void lookHome();
}
public class Demo3 {
public static void main(String[] args) {
new Dog() {
@Override
public void lookHome() {
// TODO Auto-generated method stub
System.out.println("看家比较负责任");
}
}.lookHome();
}
}
import java.util.Comparator;
import java.util.TreeSet;
class Emp {
String name;
int age;
public Emp(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Emp [name=" + name + ", age=" + age + "]";
}
}
public class Demo4 {
public static void main(String[] args) {
//借助于有参的构造方法
//TreeSet(Comparator<? super E> comparator)
//构造一个新的,空的树集,根据指定的比较器进行排序。
TreeSet<Emp> emps = new TreeSet<Emp>(new Comparator<Emp>() {
@Override
public int compare(Emp o1, Emp o2) {
// TODO Auto-generated method stub
int num = o1.age - o2.age;
return num;
}
});
emps.add(new Emp("狗蛋", 23));
emps.add(new Emp("狗蛋1", 22));
emps.add(new Emp("狗蛋2", 24));
emps.add(new Emp("狗蛋3", 21));
System.out.println(emps);
}
}