前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java基础第十三篇之Collection

java基础第十三篇之Collection

作者头像
海仔
发布2019-08-05 18:37:13
5220
发布2019-08-05 18:37:13
举报
文章被收录于专栏:海仔技术驿站海仔技术驿站

常见的几种数据结构: * 1.堆栈:先进后出 * 2.队列:先进先出 * 3.数组:查找快,增删慢 * 4.链表:查找慢,增删快 import java.util.LinkedList;

/* * java集合的根接口 Collection * 共性的方法:增删改查 * 增:add(E e);//addAll(Collection<? extends E> c) * 删:remove(Object obj); * 改:无 * 查:size() * 其他:toArray();clear();isEmpty(); * * 分为两个子接口:List<E>,Set<E> * 1.List:接口,有实现类:ArrayList<E>,LinkedList<E>,Vector<E> * 特点:a.有序的,(这里的有序 不是指123,或者abc这种自然顺序,指的存和取的顺序一致) * b.有索引(有下标) * c.元素可重复 * List接口中定义方法:增删改查 * 增加: add(E e);add(int index,E e);//在指定下标处增加一个元素 * 删除: remove(Object obj); remove(int index);//删除指定下标元素 * 修改: set(int index,E e);//把指定下标元素改为新的元素 * 查询: get(int index);//获取指定下标的元素 * List接口下各种实现类的数据结构特点: * ArrayList: 内部采用动态数组结构:查找快,增删慢 * LinkedList: 内部采用双向链表结构:查找慢,增删快 * Vector: 内部采用数组结构:查找快,增删慢 * 说明:Vector是从JDK1.0 开始有的 * Collection集合根接口从JDK1.2开始的 * =============================================== * 实际上ArrayList和Vector中基本没有特有的方法,这些实现类的方法基本上都是List中的方法 * LinkedList特有的方法: * 第一组: * addFirst(E e),addLast(E e); * 第二组: * public E removeFirst();//返回并删除首元素 * public E removeLast(); * 第三组: * public E getFirst();//返回首元素 * public E getLast(); * 第四组: * public E pop();//出栈,弹栈,就是从集合中删除元素(哪一个:集合首个元素,类似于 removeFirst) * public void push(E e);//压栈,就是向集合添加一个元素(添加到集合的第一个,类似于add,addFirst) * * * 2.Set:接口,有实现类,HashSet,LinkedHashSet,TreeSet * 特点:a.元素不重复 * b.无索引(无下标) * c.无序(有例外,LinkedHashSet,TreeSet) * Set接口中定义方法:增删改查(特别好记,Set接口中没有特有方法完全和Collection接口一模一样) * * Set接口下各种实现类的数据结构特点: * HashSet: 内部采用哈希表结构 * 特点:查找较快,增删也较快 * LinkedHashSet: 内部采用 链表结构+哈希表结构 组合结构 * 特点:查找较快,增删也较快 * HashSet和LinkedHashSet特有的方法:无 * 全部都是和Set接口中一样,和Collection接口中一样 */ public class LinkedListDemo01 {

public static void main(String[] args) { // TODO Auto-generated method stub demo01(); } /* * LinkedList特有的方法 */ public static void demo01(){ //创建一个对象 LinkedList<String> names = new LinkedList<String>(); names.add("jack"); names.add("rose"); names.add("lilei"); names.add("rose"); //添加 // names.addFirst("hanmeimei"); // names.addLast("Tom"); //获取(仅仅取出元素,但是不删除) // String s1 = names.getFirst(); // System.out.println(s1); // String s2 = names.getLast(); // System.out.println(s2); //删除元素(不仅取出元素,而且会在集合中删除) // String s1 = names.removeFirst(); // System.out.println(s1); // String s2 = names.removeLast(); // System.out.println(s2); //pop 出栈方法 // String s1 = names.pop(); // System.out.println(s1); // String s2 = names.pop(); // System.out.println(s2); //push 入栈方法 names.push("Tom"); names.push("Jirui"); System.out.println(names); } }

* * HashSet 判断元素是否重复是根据两个条件 * * 1.看新元素的哈希值 和 所有旧元素是否相同,如果不相同那么不重复,存储 * * 2.如果哈希值相同了,调用equals方法,拿新元素和哈希值相同的那个旧元素,返回值true,那么判断重复元素,不存储 * 返回值false 判断不重复 可以存储 * * * 哈希表:底层是 数组结构+链表结构 特点:查找较快,增删较快 哈希表又称散列表,是一种能将关键字映射成存储地址的记录存储技术。 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录, 以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。Hash就是找到一种数据内容和数据存放地址之间的映射关系。 * 对象的哈希值: * 实际上java中所有的对象 都有一个字符串表示:toString(),默认:包名.类名@哈希值 * 实际上java中所有的对象 都有一个数字的表示:hashCode();//返回的就是一个数字,就是我们说对象的哈希值 * * * 字符串的哈希值: * 字符串类的哈希值 自己重写了hashCode,计算哈希值只跟内容有关系,所有内容相同的字符串,哈希值必定相同 * 内容不同的字符串,哈希值还有可能相同 * * * */ public class StringHashDemo01 {

public static void main(String[] args) { // TODO Auto-generated method stub String s1 = new String("重地"); String s2 = new String("通话"); int h1 = s1.hashCode(); int h2 = s2.hashCode(); //System.out.println(h1==h2); //哈希值是根据地址值计算的,如果String类没有重写hashCode那么算出来的哈希值应该是不一样 //因为String类重写了 hashCode,所以不同的地址和hash值的计算已经没有关系 //System.out.println(s1==s2);//false System.out.println(h1);//96354 System.out.println(h2); // System.out.println(s1); // System.out.println(s2); //我们看了String类的hashCode方法,字符串的哈希值,只跟内容有关系 //可能不可能出现 字符串的内容不同 但是哈希值相同 //比如:"abc"和"acD" //比如: "重地"和 "通话" } /* * 对象的哈希值 */ public static void demo01(){ //创建Person对象 Person p1 = new Person(); int hash = p1.hashCode(); //hash就是p1的哈希值 System.out.println(hash); System.out.println(p1.toString()); //我们骗了19天,打印对象默认打印出来的不是地址值 是hash值 //哈希值是根据地址值算出来的,怎么算的我们不知道 //实际上 由地址值 计算哈希值 由一个算法计算 散列算法---哈希算法 //在我们java程序中能不能打印出对象 真正的地址值(不能) //但是地址值 是真正存在的 对象中存储的就是真正的地址值 但是打印的时候打印出来是哈希值 Person p2 = new Person(); int hash2 = p2.hashCode(); System.out.println(hash2); //不同的对象 地址值肯定不一样,但是哈希值有可能一样吗? }

}

/* * 使用HashSet存储自定义的元素:存储Student对象 * * 我们要求:如果一个学生对象的年龄和姓名都相同,我们不让HashSet存储它 * * 以后写一个类: * 1.封装: * 2.构造(全参和无参) * 3.toString(方便输出对象) * 如果这个类还要存储到集合中最好 * 4.hashCode * 5.euqals * */ public class Student { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student() { super(); // TODO Auto-generated constructor stub } public Student(int age, String name) { super(); this.age = age; this.name = name; } @Override public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; 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; Student other = (Student) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }

//自己重写hachCode // 20 abc // 20 acD // 19 acE // @Override // public int hashCode() { // //我们目的,让年龄相同并且名字相同的对象返回的哈希值也相同 // return this.age * 31 + this.name.hashCode()*49; // } //Eclipse自动生成的hashCode //自己重写equals方法,让年龄相同,名字相同两个对象调用equals返回值是true // @Override // public boolean equals(Object obj) { // // TODO Auto-generated method stub // Student s = (Student)obj; // if(this.age != s.getAge()){ // return false; // } // if(!this.name.equals(s.getName())){ // return false; // } // return true; // } //Eclipse自动生成的equals方法 }

/* * LinkedHashSet和HashSet的不同点: * HashSet是无序的 * LinkedHashSet是有序的 * * contains方法和add方法的原理 * * 1.如果是ArrayList存储自定义元素 ,那么这个自定义类型 只要重写 equals * * 2.如果是HashSet存储自定义元素,那么这个自定义类型 必须重写两个 hashCode,equals * */

/* * Collection<E>接口: * add(E e) remove(Object o),size(),toArray();isEmpty();contains() * 迭代器 * 获取迭代器: Iterator<E> it = 集合对象.iterator(); * 使用迭代器: * it.hasNext(); it.next(); * * List<E>和Set<E> * 能够说出List集合特点:有序,索引和重复 * 能够说出Set集合的特点:无索引,不重复,无序的(但是LinkedHashSet,TreeSet是有序的) * * 使用List存储的数据结构:ArrayList是数组结构 LinkedList是链表结构 * * 说出哈希表的特点:数组结构+链表结构: 查找较快,增删较快 * * 使用HashSet集合存储自定义元素(保证元素的内容相同不能存储) * 快捷键:alt+shift+s,h * * 说出判断集合元素唯一的原理 * Collection中有contains(Object o); * ArrayList中 只调用equals比较 * HashSet中 调用hashCode 和 equals * * 冒泡排序:把一个数组里面的内容 按照从小到大 或者从大到小

*/ public class BubbleDemo { public static void main(String[] args) { int[] nums = {7,65,10,532,47,45,234,84,3}; int len = nums.length; //外层控制趟数 for (int i = 0; i < len-1; i++) { //里控制次数 for(int j = 0;j < len-1-i;j++){ //nums[j] 和 nums[j+1] if(nums[j] > nums[j+1]){ int temp = nums[j]; nums[j] = nums[j+1]; nums[j+1] = temp; } } } for (int i = 0; i < nums.length; i++) { System.out.println(nums[i]); } } }

冒泡排序优化版:

package com.baidu_01;

import java.util.Arrays;

public class Test3 { public static void main(String[] args) { //int[] arr = {1,8,5,3,7,6}; int[] arr = {1,2,3,4,5,6}; boolean b = true ; for(int i = 0; i < arr.length-1 ; i++) { //b = true; for(int j = 0; j < arr.length - 1 -i;j++) { if(arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; b = false; } } if(b) { System.out.println("遍历了一次"); break; } } System.out.println(Arrays.toString(arr)); } }

3.TreeSet类

TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。向TreeSet中添加的应该是同一个类的对象,且最好是不可变对象。

1.自然排序

自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。

Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。

obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。

如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0

2.定制排序

自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表示o1大于o2 ;如果方法返回0,则表示o1等于o2,如果该方法返回负整数,则表示o1小于o2。 Java静态代码块、构造代码块、构造方法的执行顺序 的执行顺序

静态代码优先于非静态的代码,是因为被static修饰的成员都是类成员,会随着JVM加载类的时候加载而执行,而没有被static修饰的成员也被称为实例成员,需要创建对象才会随之加载到堆内存。所以静态的会优先非静态的。 执行构造器(构造方法)的时候,在执行方法体之前存在隐式三步: 1,super语句,可能出现以下三种情况: 1)构造方法体的第一行是this语句,则不会执行隐式三步, 2)构造方法体的第一行是super语句,则调用相应的父类的构造方法, 3)构造方法体的第一行既不是this语句也不是super语句,则隐式调用super(),即其父类的默认构造方法,这也是为什么一个父类通常要提供默认构造方法的原因; 2,初始化非静态变量; 3,构造代码块。 由此可知,构造代码块优先于构造方法的方法体,但是this关键字跟super关键字不能同时出现,而且只能在代码的第一行。如果出现了this关键字,隐式三步就不会执行。 例如,分析下面的代码及执行结果,已经用注释标出了执行步骤Step 1–Step 7。 也就是说,当递归调用多个构造方法的时候,构造代码块只会在最后的(也即方法体第一行不是this语句的)那个构造方法执行之前执行!

public class Test { public static int a = 0;

static {// Step 1 a = 10; System.out.println("静态代码块在执行a=" + a); }

{// Step 4 a = 8; System.out.println("非静态代码块(构造代码块)在执行a=" + a); }

public Test() { this("调用带参构造方法1,a=" + a); // Step 2 System.out.println("无参构造方法在执行a=" + a);// Step 7 }

public Test(String n) { this(n, "调用带参构造方法2,a=" + a); // Step 3 System.out.println("带参构造方法1在执行a=" + a); // Step 6 }

public Test(String s1, String s2) { System.out.println(s1 + ";" + s2);// Step 5 }

public static void main(String[] args) { Test t = null;// JVM加载Test类,静态代码块执行 System.out.println("下面new一个Test实例:"); t = new Test(); } public boolean contains(Object c) : 判断是否包含元素 public boolean isEmpty() : 判断是否为空 public int size() : 获取集合长度 Collection中主要方法: boolean add(E e) : 添加元素 boolean remove(Object o) : 删除元素 void clear() : 清空集合 boolean contains(Object o1) : 判断是否包含某元素 boolean isEmpty () : 判断是否为空 int size() : 获取集合长度 } 1 33 执行结果:

静态代码块在执行a=10 下面new一个Test实例: 非静态代码块(构造代码块)在执行a=8 调用带参构造方法1,a=10;调用带参构造方法2,a=10 带参构造方法1在执行a=8 无参构造方法在执行a=8 */

/*

Arrays : 查API static void sort(Object[] obj) : 对传进来的基本类型数组进行排序 static void toString(Objec[] a) : 对传入的数组内容以字符串的形式表现出来. 方法重写:字符类出现了一摸一样的方法(注意:返回值类型可以是子父类)

Override和Overload的区别?Overload能改变返回值类型吗? overload可以改变返回值类型,只看参数列表.

方法重载:本类中出现的方法名一样,参数列表不同的方法,与返回值类型无关.

子类对象调用方法的时候: 先找子类本身,再找父类. final关键字修饰局部变量:

1.基本类型,是指不能被改变. 2.引用类型,是地址值不能被改变,对象中的属性可以改变

final修饰变量的初始化时: 1.显示初始化; 2.在对象构造完毕前即可

面试:

要求使用已知的变量,在控制台输出30,20,10

class Outer { public int num = 10; class Inner { public int num = 20; public void show () { int num = 30; syso(); syso(); syso(); } } } class InnerClassTest { public static void main(String[] args ) { Outer.Inner oi = new Outer().new Inner(); oi.show(); } }

package , import , class 顺序为:package - import - class 按照这个顺序执行

代码块的概述:在java中,使用{}括起来的代码被称为代码块.

A.代码块分为:局部代码块,构造代码块,静态代码块,同步代码块

代码块应用:

a.局部代码块:在方法中出现;限定变量声明周期,及早释放,提高内存利用率.

b.构造代码块(初始化块) : 在类中方法外出现;多个构造方法中相同的代码块放到一起,每次调用构造都指向,并且在构造方法前指向

c.静态代码块:在类中方法外出现,并加上static 修饰;用于给类进行初始化,在加载的时候就指向,并且只执行一次. 一般用于加载驱动. 多态调用方法这么执行: 成员变量:编译看左边(父类),运行看左边(父类); 成员方法:编译看左边(父类),运行看右边(子类).动态绑定 静态方法:编译看左边(父类),运行看左边(父类); (静态和类相关,算不上重写,所以,访问换是左边的) 只有非静态方法的成员方法,编译看左边,运行看右边. 内部类访问特点: 外部类访问内部类必须创建对象. 外部类名.内部类名 对象名 = 外部类对象.内部类对象

静态成员内部类:

static :成员内部类被静态修饰后的访问方式是: 外部类名.内部类名 对象名 = 外部类.内部类对象;

面试: class A { public void show () { show2(); } public void show2() { syso("我"); } } class B extends A{ show(){ show2(); } show 2 (){ syso("爱") } } class c extends B{ show(){ show2(); } show 2 (){ syso("你") } } public class Test { main { A a = new B(); a.show(); B b = new C(); b.show(); } }

面试题: 一个抽象类如果没有抽象方法,可不可以订阅为抽象类,?有什么意思? 可以,这么做只有一个目的,就是不让其他类创建本类对象,交给子类完成.

class Test { public static void main(String[] stgs ) { //Outer.method().show(); Inter i = Outer.method(); i.show(); } } //按照要求补齐代码 interface Inter { void show(); } class Outer { public static Inter method() { return new Inter() { public void show() { syso("show"); } } } }

方法重写注意事项: 1.父类中私有方法不能被重写.(父类私有方法子类根本就无法继承) 2.子类重写父类方法时,访问权限不能更低. 最后就一致 3.父类静态方法,子类也必须通过静态方法进行重写. 其实这个算不算方法重写,但是现象确实如此,至于为什么算不上方法重写 子类重写父类方法,最好声明一摸一样.

例题: class Fu { public Person show() { } } class Zi extends Fu { public Student show() { } } class Person { } class Student extends Person { }

运算符:instanceof : 用法: 是双目运算符,左面的操作是一个对象实例,右面是一个类.当左面的对象是右面的类创建的对象时,该运算符 运算的结果是true,否则是false;

说明: 1.一个类的实例包括本身的实例,以及所有直接或间接子类的实例 2.instanceof左边操作元显示的类型与右边操作元必须是同种类或有继承关系 即位于继承树的同一个分支上,否则会编译出错.

冒泡排序:让数组中的元素,从小到大存储.

相邻比较思想. 我们需要比较多少趟 第一趟: 我们要比较几次 len-1; 冒泡排序 : 如果一个数组的长度还是len 那么要比较 len-1趟 下标 0-1 //控制躺数 下标 1-2 for(int i = 0 ; i < len-1; i ++) { 下标 2-3 for(int j = 0 ; j < len-1; j++) { 下标 3-4 比较num3[j]和num3[j+1] 下标 4-5 下标 5-6 } }

第二趟: len-1-1; 0-1 1-2 2-3 3-4 4-5

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年06月30日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档