目录
集合
概述
1.1什么是集合(引用数据类型)?有什么用?
数组其实就是一个集合,集合实际上就是一个容器,可以用来容纳其他类型的数据。
集合为什么说在实际开发中使用较多
集合是一个容器,一次容纳多个对象。在实际开发中,假设连接数据库,数据库中有十条记录,假设把这10条记录查询出来,在Java程序中会将10条数据封装成10个对象,然后将10个Java对象放到某一个集合中,将集合传到前端,然后遍历集合,将一个一个数据展现出来。
1.2集合不能直接存储基本数据类型,集合也不能直接存储引用数据类型。
为什么数组可以直接存储基本数据类型呢?
因为数组可以定义为基本数据类型数组,而集合只能定义为引用类型。
1.3 在Java中每一个不同的集合,底层对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构中。例如:数组,二叉树,链表,哈希表以上都是常见的数据结构。
1.4 集合在Java JDK中java.utl
1.5 为了更好掌握集合,最好背会继承结构图。
在Java集合中存储数据以单个数据存储的方式,那么它的超级父类是java.utl.collection
iterator方法用于返回一个实现了 Iterator 接口的对象
Arraylist:
1.初始容量是10,底层是一个object类型数组
2.Arraylist有三个构造方法
public ArrayList():默认的数组容量是10。(底层先创建一个长度(容量)为0的数组,当添加第一个元素的时候,初始化容量为10。)
public ArrayList(int initialCapacity):指定数组容量为initialcapacity
public ArrayList(Collection<? extends E> c)
public class CollectionTest {
public static void main(String[] args) {
//默认容量是10
List Mylist1 = new ArrayList();
//指定容量是100
List Mylist2 = new ArrayList(100);
//
Collection c = new HashSet();
c.add(100);
c.add(200);
c.add(900);
c.add(50);
List mylist3 = new ArrayList(c);
for (int i = 0; i <= mylist3.size(); i++){
System.out.println(mylist3.get(i));
}
}
}
linkedlist是双向链表
vector与ArrayList的区别:
1.vector起始容量也是10,扩容是原来容积的两倍。而Arraylist起始容积也是10,但是扩容是原来的1.5倍。建议提取预估数组容量,尽量不要扩容,因为扩容效率太低。
2.vector是线程安全的,Arrylist是线程不安全的。因为Vector中所有的方法都是线程同步的都带有synchronized关键字,所以vector的效率不高很少使用。
怎么将一个线程不安全的ArrayList集合转换成Vector线程安全的集合???
使用集合工具类:java.utl.collections.
collection是集合接口
public class CollectionTest {
public static void main(String[] args) {
List list = new ArrayList();//非线程安全的
//变成线程安全的
Collections.synchronizedList(list);
list.add("111");
list.add("222");
list.add("333");
}
}
set集合:无序不可重复:无序是存放进去的顺序和取出来的顺序不一样。不可重复是存储数据不能重复,但是如果重复了编译器不会报错,只是在运行阶段,那些重复的数据好像消失了。
hashset:无序不可重复
public class CollectionTest{
public static void main(String[] args) {
Set<String> strs = new HashSet<>();
strs.add("hello3");
strs.add("hello4");
strs.add("hello1");
strs.add("hello2");
strs.add("hello3");
strs.add("hello3");
strs.add("hello3");
strs.add("hello3");
strs.add("hello3");
for (String s : strs){
System.out.println(s);
}
}
}
Treeset: 无序不可重复,但是存储元素可以自动按照大小顺序排序,称为可以排序集合。
public class CollectionTest{
public static void main(String[] args) {
Set<String> strings = new TreeSet<>();
strings.add("A");
strings.add("B");
strings.add("Z");
strings.add("Y");
strings.add("Z");
strings.add("K");
strings.add("M");
for (String s : strings){
System.out.println(s);
}
}
}
在Java集合存储数据以键值对的方式存储数据,那么它的超级父类是java.utl.map
Map接口中常用方法
public interface Map<K,V> key和value都可以指定类型
void clear():清空Map集合
boolean containsKey(Object key):判断Map中是否包含某个Key
boolean containsValue(Object value):判断Map中是否包含某个value
V get(object key):通过key获取value
boolean isEmpty()判断Map集合中元素个数是否为0
set(k)keySet()获取Map集合中所有的Key
V put(key,value):向Map集合中添加键值对
V remove(Object key):通过Key删除键值对
int size():获取Map集合中键值对的个数
Collection<V> values():获取Map中所有的Value返回一个Collection集合(由所有的Value构成)
Set<Map.Entry<K,V>> entrySet():将map集合转换成set集合。
假设现有一个Map集合,如下图所示:
key | value |
1 | zhangsan |
2 | lisi |
3 | wangwu |
4 | zhaoliu |
可以通过一个entrySet()方法将一个map集合转换成set集合,如下面的1 = zhangsan(对象),这个对象的类型是Map.Entry,Entry是Map接口中的一个静态内部类
public class CollectionTest{
//静态内部类
private static class Innerclass{
//静态方法
public static void m1(){
System.out.println("静态内部类m1方法执行");
}
public void m2(){
System.out.println("静态内部类中实例方法执行");
}
}
public static void main(String[] args) {
CollectionTest.Innerclass.m1();
//静态内部类怎么实例化
CollectionTest.Innerclass innerclass = new CollectionTest.Innerclass();
innerclass.m2();
//用来理解Set<Map.Entry<K,V>>
Set<MyMap.Myentry<Integer, String>> set = new HashSet<>();
}
}
class MyMap{
public static class Myentry<k,V>{
}
}
在Set set = Map.entrySet()之后;
生成了set集合对象
1 = zhangsan
2 = lisi
3 = wangwu
4 = zhangliu
测试方法map接口中的方法
public class CollectionTest{
public static void main(String[] args) {
//创建Map集合对象
Map<Integer, String> map = new HashMap<>();
//向Map集合添加键值对:put方法
map.put(1,"lizude");//这里的1自动装箱
map.put(2,"lisi");
map.put(3,"wanggwu");
map.put(4,"zhaoliu");
//通过key获取value:get方法
for (int i = 1; i <= 4; i++){
String s = map.get(i);
System.out.println(s);
}
//获取键值对的数量 : size
System.out.println(map.size());
//通过key删除value: remove
map.remove(1);
System.out.println(map.size());
for (int i = 1; i <= 4; i++){
String s = map.get(i);
System.out.println(s);
}
//判断是否包含某个key和判断包含某个value: containskey 与 containsvalue
//contains方法底层都是调用equals方法比对,所有自定义类型必须重写equals方法
System.out.println(map.containsKey(1));
System.out.println(map.containsValue("zhaoliu"));
//获取所有value: values返回值是一个集合
Collection<String> collection = map.values();
for (String s : collection){
System.out.println(s);
}
//获取Map集合所有的key(返回的是一个set集合)
Set<Integer> set = map.keySet();
for (Integer integer : set){
System.out.println(integer);
}
//将map集合转换成set集合
Set<Map.Entry<Integer,String>> set1 = map.entrySet();
for (Map.Entry<Integer,String> q : set1){
System.out.println(q);
}
//清空Map集合和判断map集合是否为空:clear 与 isEmpty。
map.clear();
System.out.println(map.size());
System.out.println(map.isEmpty());
}
}
map集合的遍历
//遍历Map集合遍历
public class CollectionTest{
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"lizude");
map.put(2,"wufei");
map.put(3,"longer");
map.put(4,"lisi");
//第一种方式,获取所有的key,通过遍历key来遍历value
Set<Integer> keys = map.keySet();
//遍历key,获取value
//迭代器
Iterator<Integer> iterator = keys.iterator();
while(iterator.hasNext()){
Integer key = iterator.next();
System.out.println(map.get(key));
}
//foreach
for (Integer key1 : keys){
System.out.println(map.get(key1));
}
//第二种方式遍历Map集合
//以上方法是直接将Map集合直接全部转换成set集合
//set集合中类型是Map.entry
Set<Map.Entry<Integer,String>> set1 = map.entrySet();
//for循环增强
//这种方式效率高,因为获取value获取key都是从Node对象中获取的属性值,因为integerStringEntry是Map.Entry<Integer,String>接口的对象,实现Map.Entry<Integer,String>接口的是一个Node类。这种方式适合大数据量。而下面迭代器还需要去哈希表中找效率低。
for (Map.Entry<Integer,String> integerStringEntry : set1){
System.out.println(integerStringEntry);
}
//迭代器
Iterator<Map.Entry<Integer,String>> iterator1 = set1.iterator();
while (iterator1.hasNext()){
Map.Entry<Integer,String> entry = iterator1.next();//这里iterator1.next()返回的是一个Map.Entry<Integer,String>类型对象,而Map.Entry<Integer,String>是一个接口,要成为对象必须要将Map.Entry<Integer,String>接口实现,
// 在hashmap源码中有一个node类将Map.Entry<Integer,String>接口实现了。这里的entry对象其实是一个Node类型的。
Integer key3 = entry.getKey();
String value = entry.getValue();
System.out.println(key3 + "=" + value);
}
}
}
collection接口中常用方法
collection可以存放什么类型数据:在没有使用泛型之前,collection存放所有object所有的子类类型。
使用了“泛型”之后,collection中只能存储某个具体的类型.
//add添加单个元素
//addall添加多个元素
//remove删除单个元素,可以指定删除元素,也可以依靠下标删除某个元素
//removeall是删除多个元素
//contains:查找某个元素是否存在
//containsall查找多个元素是否存在
//size可以返回当前集合元素的个数,不是获取集合容量
//clear是清空list中所有元素
import java.util.ArrayList;
import java.util.List;
public class CollectionTest {
public static void main(String[] args) {
List list = new ArrayList();
//add添加单个元素
list.add("ssss");
list.add(11);//add方法里面的要传的参数是object,但是你可以这里想传一个int类型的参数进去,你可以把int类型包装成Intrger类,实际上是new Integer(11)或者 integer i = 11(自动装箱).
list.add(true);
System.out.println("list = " + list);//list = [ssss, 11, true],这里面的11,true不是基本数据类型了,而是引用数据类型,因为自动装箱机制
//remove删除单个元素,可以指定删除元素,也可以依靠下标删除某个元素
list.remove(0);
list.remove(new Integer(11));
System.out.println("list = " + list);
//contains:查找某个元素是否存在
System.out.println(list.contains(true));
//size可以返回元素的个数
System.out.println(list.size());
//clear是清空list中所有元素
list.clear();
//addall添加多个元素
List list1 = new ArrayList();
list1.add("红楼梦");
list1.add("三国");
list.addAll(list1);
System.out.println(list);
//containsall查找多个元素是否存在
System.out.println(list.containsAll(list1));
//removeall是删除多个元素
list.add("聊斋");
list.removeAll(list1);
System.out.println(list);
}
}
深入研究contains和remove方法
boolean contains(object o)
判断集合中是否包含某个对象o
如果包含返回true,如果不包含返回false
public class CollectionTest {
public static void main(String[] args) {
Collection c = new ArrayList();
String s1 = new String("abc");
c.add(s1);
String s2 = new String("def");
c.add(s2);
String x =new String("abc");
System.out.println(c.contains(x));//true
}
}
public class CollectionTest {
public static void main(String[] args) {
User u1 = new User("jack");
Collection c = new ArrayList();
c.add(u1);
User u2 = new User("jack");
//未重写equals方法之前
System.out.println(c.contains(u2));//false
//重写equals方法
System.out.println(c.contains(u2));//true
}
}
class User{
private String name;
public User(){}
public User (String name){
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}
}
放在集合中的equals必须要重写,因为集合中存放的是引用数据类型。集合中contains方法是看内容,而不是地址。包不包含某个具体的内容
remove()方法底层也调用了equals方法,因此也要重写equals方法。
collection集合的迭代器
迭代就是遍历集合。这种遍历集合的方式是通用方式,除map集合之外。Iterable接口有一个Iterator方法,可以返回一个实现了Iterator接口方法的对象,这个对象有三个方法:
hashnext():这个方法返回布尔类型,如果返回true代表还可以迭代,集合后面还有元素可以继续迭代。如果返回false表示没有更多的元素可以迭代了。
next():这个方法返回集合中的对象。这个方法让迭代器前进一位,并且将指向的元素返回。
remove():这个remove与collection接口中remove的区别:
其实每次
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
//添加元素
collection.add("abc");
collection.add("def");
collection.add(100);
collection.add(new Object());
//对集合collection进行遍历/迭代
//第一步:获取集合对象的迭代器对象Iterator
Iterator it = collection.iterator();//集合对象中有个iterator方法可以返回一个实现了Iteraeter接口的对象。
//第二步:通过以上获取的迭代器对象开始迭代遍历集合。
while(it.hasNext()){
System.out.println(it.next());
}
}
}
List集合特有的方法
1.List集合存储元素特点:有序可重复
有序:List集合中元素有下标。从0开始,以1递增
可重复:可存储一个1,还可以存储1.
2.List既然是collection接口的子接口,那么肯定List接口有自己特色的方法。
以下只列出list特有且常用的方法:
ublic class CollectionTest {
public static void main(String[] args) {
List mylist = new ArrayList();
//添加元素
//默认集合末尾加元素
mylist.add("A");
mylist.add("B");
mylist.add("c");
mylist.add("D");
//list中特有的方法
//在指定下标位置加入元素,下标位置元素以及后面元素后移。
//这个方法对于Arraylist集合来说该方法效率低使用不多,因为ArrayList的底层是数组,数组的的空间是连续存储的,需要后面元素全部后移。
mylist.add(1,"KING");
//获取下标元素(list集合特有的,set集合没有,因为set无序没有下标)
System.out.println(mylist.get(0));
//list特有遍历集合的方式
for (int i = 0; i < mylist.size(); i++){
System.out.println(mylist.get(i));
}
//获取指定对象第一次出现处的索引
System.out.println(mylist.indexOf("D"));
//获取指定对象最后一次出现处的索引
System.out.println(mylist.lastIndexOf("D"));
//删除指定下标元素
mylist.remove(0);
System.out.println(mylist.size());
//修改指定下标元素
mylist.set(0,"dddd");
//迭代
Iterator iterator = mylist.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
泛型机制(jdk5之后)
泛型概述
1.泛型机制作用:指定集合中存储元素的类型。
2.泛型这种语法机制,只在程序编译阶段起作用,只是编辑器参考的(运行阶段泛型没用)。
3.泛型的好处:集合中存储的元素统一了。
集合中取出的元素是泛型指定的元素(如果不加泛型取出的是object类型元素),而且不需要进行大量的“向下转型”。
泛型的缺点:集合中存储的元素缺乏多样性。可以存储泛型指定类型的子类型
不使用泛型:
public class CollectionTest {
public static void main(String[] args) {
//不使用泛型机制,分析程序存在缺点
List list = new ArrayList();
cat c = new cat();
bird b = new bird();
//将对象添加到集合中
list.add(c);
list.add(b);
Iterator it = list.iterator();
while(it.hasNext()){
Object obj = it.next();//规定next()方法返回的是一个object对象,如果你定义了泛型但是却不使用,那么他默认指定类型是object。这里是加泛型与不加的泛型的区别。
if(obj instanceof cat){
cat c1 = (cat)obj;
c1.catchMouse();
}
if(obj instanceof bird){
bird b1 = (bird)obj;
c1.fly();
}
if(obj instanceof animal){
animal a1 = (animal)obj;
a1.fly();
}
}
}
}
class Animal{
}
class cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class bird extends Animal{
public void fly(){
System.out.println("鸟儿在飞行");
}
}
使用泛型:
public class CollectionTest {
public static void main(String[] args) {
//在jdk5之后使用泛型机制
//使用泛型List<Animal>之后,表示list集合中只允许存Animal数据
//使用泛型指定集合中存储的元素
List<Animal> list = new ArrayList<Animal>();
cat c = new cat();
bird b = new bird();
list.add(c);
list.add(b);
//表示迭代器迭代的都是Animal类型
Iterator<Animal> it = list.iterator();
while(it.hasNext()){
//使用泛型之后,每一次迭代返回的数据都是Animal类型,不需要强制类型转换
Animal animal = it.next();
animal.printa();
if (animal instanceof cat){
cat c1 = (cat)animal;
c.catchMouse();
}
if (animal instanceof bird){
bird b1 = (bird) animal;
b1.fly();
}
}
}
}
class Animal{
public void printa(){
System.out.println("是动物");
}
}
class cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class bird extends Animal{
public void fly(){
System.out.println("鸟儿在飞行");
}
}
自动类型推断(jdk8之后)
public class CollectionTest {
public static void main(String[] args) {
//这里的ArrayList<这里的类型会自动推断>,前提是JDk8之后才允许
//自动类型推断
List<Animal> mylist = new ArrayList<>();
}
}
class Animal{
}
自定义泛型
//public class CollectionTest<jkdajkdlalsda>的<>中只是标识符,可以随便写,然后在new对象时指定传入的泛型。
// 其实迭代器源码中就使用了泛型的标识符,所以才会有后面指定泛型,返回泛型指定对象
//在源码中用的最多的标识符是<T> T是type单词首字母,<E> E是Element单词首字母。
public class CollectionTest<jkdajkdlalsda> {
public static void main(String[] args) {
CollectionTest<String> c = new CollectionTest<>();
c.dosome("abc");
CollectionTest<Integer> c1 = new CollectionTest<>();
c1.dosome(100);
MyIterator<String> mi = new MyIterator<>();
String s = mi.get();
//如果你定义了泛型但是却不使用,那么他默认指定类型是object
MyIterator mm = new MyIterator();
Object o = mm.get();
}
public void dosome(jkdajkdlalsda o){
System.out.println(o);
}
}
class MyIterator<T>{
public T get(){
return null;
}
}
增强for循环(Jdk5.0之后的新特性)
基本结构: for(元素类型 变量名 :数组或者集合){循环语句}
public class CollectionTest{
public static void main(String[] args) {
//数组
int[] arr = {1,2,3,4,5,6,7,8,9};
//遍历数组
//普通for循环
// for (int i = 0; i < arr.length; i++){
// System.out.println(arr[i]);
// }
//增强for循环
//foreach有个缺点:没有下标,在需要下标的循环中不建议使用
for (int data : arr){
//data是数组中的每个元素
System.out.println(data);
}
//集合
List<String> strlist = new ArrayList<>();
strlist.add("hello");
strlist.add("world");
strlist.add("lizude");
Iterator<String> iterator = strlist.iterator();
//使用迭代器遍历
while(iterator.hasNext()){
String s = iterator.next();
System.out.println(s);
}
//使用for循环遍历
for (int i = 0;i < strlist.size(); i++){
System.out.println(strlist.get(i));
}
//使用增强for循环
//s代表集合中的元素
for (String s : strlist){
System.out.println(s);
}
}
}
哈希表的数据结构
1.哈希表是数组和单向链表的结合体:一维数组,数组中每一个元素是一个单向链表。
数组查询效率高,链表随机增删方面效率很低。
哈希表把以上两种数据结构结合在一起,充分发挥他们各自的优点。
2.从源码刨析哈希表
public class HashMap<K,V>{
//Hashmap底层实际上就是一个数组。(一维数组)
transient Node<K,V>[] table;
//静态内部类
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//哈希值(哈希值是key的hashcode()方法的执行结果。哈希值通过哈希算法,可以将哈希值转换成数组下标)
final K key;//存储到map集合中的那个key
V value;//存储到Map集合中的那个value
Node<K, V> next;//下一个节点的内存地址
}
}
图形表示:
因为object中的hashcode方法 是返回一个对象的哈希值,不同的对象返回的hash值不一样,就算是相同的值,当时返回的对象哈希值不一样,因此要重写该类的hashcode方法,只要他们的内容一样他们就返回相同的hash值,因为hashset或者hashmap都是无序不可重复的集合
为什么需要同时重写hashcode和equals方法
1.放在hashmap集合key的部分元素,以及放在hashset集合中的元素,需要同时重写hashcode和equals方法。重写hashcode方法是保持hashset和hashmap不可重复的特点的关键,因为如果不重写就会就算对象内容相同也会出现相同的元素在不可重复的hashset和hashmap集合中。
2.hashMap默认初始化容量是16,默认加载因子是0.75,这个默认加载因子是当hashmap集合底层数组容量达到75%时,数组开始扩容。
重点:记住hashMap集合必须是2的倍数,这是因为这样才能达到散列均匀,为了提高hashmap集合存取效率,所必须的。
public class CollectionTest{
public static void main(String[] args) {
student s1 = new student("zhangsan");
student s2 = new student("zhangsan");
System.out.println(s1.equals(s2));//false:因为student类没有重写equals方法
//没有重写父类Object类中的hashcode方法
//返回的是s1,s2的hash值,如果不重写父类的hashcode不同的对象返回hash值不一样。
//为什么一定要重写父类的hashcode方法?因为如果不重写父类的hashcode不同的对象返回hash值不一样,那么经过哈希算法得到的下标也不一样,就会产生两个下标,就算对象的内容相同,
// 也会产生两个下标,这个两个元素内容相同却被加入到hashset或者hashmap中,但hashset或者hashmap是无序不可重复,因此重写equals方法的同时要重写hashcode方法
//如果一个类的equals方法重写了,那么hashdode也一定要重写
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
Set<student> set = new HashSet<>();
set.add(s1);
set.add(s2);
//在重写hashcode方法之前
System.out.println(set.size());//2
//在重写hashcode方法之后
System.out.println(set.size());//1
}
}
class student{
private String name;
public student(String name) {
this.name = name;
}
public student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof student)) return false;
student student = (student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
Jdk8之后对hashmap的改进:如果单向链表存储的元素超过八个就会把单向链表变成二叉树或者红黑树,当红黑树上节点小于六时候就会把红黑树变成单向链表。为了提高检索效率。
hashtable与hashmap的区别
1.在面试中面试官可能问你,hashmap集合key部分允许存储null吗?
允许key和value都为null
但是要注意:hashmap集合的key null值只能有一个。
public class CollectionTest{
public static void main(String[] args) {
Map map = new HashMap();
//hashmap集合允许key为null
map.put(null,null);
System.out.println(map.size());//1
//key重复value被覆盖
map.put(null,100);
System.out.println(map.size());//1
//通过key获取value
System.out.println(map.get(null));//100
}
}
hashtable的key,value值都不能为空,会出现nullpointexception
public class CollectionTest{
public static void main(String[] args) {
Map map = new Hashtable();
map.put(null,111);
}
}
2.hashtable是线程安全,hashtable的初始化容量是11,默认加载因子是0.75.
properties集合
目前我们只需要掌握Properties属性类对象的一些方法即可。
properties是一个map集合,继承了hashtable,properties的key和value是String类型的
properties是被称为属性类对象是线程安全的
public class CollectionTest{
public static void main(String[] args) {
Properties properties = new Properties();
//需要掌握两个方法,其实就是存和取
//存
properties.setProperty("url", "dhf");
properties.setProperty("drive", "jdbc");
properties.setProperty("username", "root");
properties.setProperty("password", "123");
//取
//通过key取value
String s = properties.getProperty("url");
System.out.println(s);
}
}
treeset集合和treemap集合
综述
1.TreeSet集合底层实际上是一个TreeMap
2.TreeMAp底层是一个二叉树
3.放到treeset集合中的元素,等同于放到Treemap集合key部分了。
4.Treeset集合中的元素:无序不可重复,但是可以按照元素大小顺序自动排序,但是自定义的类型无法排序会报错,因为没有指定排序规则(除非自己写排序的规则),可以对Interger和String类型排序。
报错的源码分析:
Comparable<? super K> k = (Comparable<? super K>) key;
因为person不是Comparable类型,所以会发生classcastexception异常。 为什么Integer和String类型可以自动排序?因为实现了comparable接口,重写了compareTo方法,因为如果不重写,那么自定义类和comparable接口就没有关系,就不能强制类型转换。
//如果是自定义的类型怎么让其自动排序,如果不实现comparable接口将发生classcastexception异常。为了解决这个问题,你就需要在自定义的类中实现该接口。
public class CollectionTest{
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(30);
Person p3 = new Person(10);
Person p4 = new Person(40);
TreeSet<Person> treeSet = new TreeSet<>();
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3);
treeSet.add(p4);
for (Person a : treeSet){
System.out.println(a);
}
}
}
class Person implements Comparable<Person>{
int age;
public Person(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
public int compareTo(Person person){
//c1.compareto(c2);
//this是c1,person是c2
//拿着参数k与集合中另外一个参数k进行比较,返回的可能是>0 <0 =0
//比较规则最终还是由程序员指定,例如按照年龄升序还是按照年龄降序
return this.age - person.age;
}
}
//自动排序
public class CollectionTest{
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>();
//添加String
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
ts.add("zhangsi");
ts.add("wangliu");
for (String s : ts){
System.out.println(s);
}
}
}
比较规则应该怎么写
public class CollectionTest{
public static void main(String[] args) {
TreeSet<Vip> vips = new TreeSet<>();
vips.add(new Vip(20,"lizude"));
vips.add(new Vip(20,"lisi"));
vips.add(new Vip(18,"18"));
vips.add(new Vip(17,"dd"));
for (Vip vip : vips){
System.out.println(vip);
}
}
}
class Vip implements Comparable<Vip>{
int age;
String name;
public Vip(int age, String name) {
this.age = age;
this.name = name;
}
//年龄升序排,如果年龄相同就按照姓名Ascll码排
public int compareTo(Vip vip){
if(this.age == vip.age){
return this.name.compareTo(vip.name);
}else{
return this.age - vip.age;
}
}
@Override
public String toString() {
return "Vip{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
自平衡二叉树
1.Treeset或者TreeMap是自平衡二叉树,遵循左小右大规则存放。
2.遍历二叉树有三种方式:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
根的位置代表不同的遍历方式
3.TreeSet或者TreeMap集合采用的是:中序遍历方式
Iterator采用的是中序遍历方式
collections工具类
public class CollectionTest{
public static void main(String[] args) {
//ArrayList集合不是线程安全的
List<String> list = new ArrayList<>();
//变成线程安全的方法
Collections.synchronizedList(list);
//排序方法
list.add("abc");
list.add("abe");
list.add("abf");
list.add("abx");
Collections.sort(list);
for (String s : list){
System.out.println(s);
}
//
}
}