位运算
科普一个位运算符的点:
<<
读作左移,丢弃最高位,右边补0,就相当于乘以2^n
>>
读作右移,最高位是0,左边补齐0,最高位是1,左边补齐1,相当于除以2^n
,当除不尽的时候就要自己算了
>>>
读作无符号右移,无论最高位是0还是1,左边都补0,相当于除以2^n
.题目1: 请用最快的方式实现两个整数变量的交换
位运算速度是最快的
当进行异或运算时,出现奇数次的数就是最后的结果,例如
a ^ b ^ a
其结果为a
.
int a = 5, b = 15;
a = b ^ a ^ a;
此时a = 15;
题目2: 编写一个函数讲一个十六进制的字符串转换为整数返回
public static void main(String[] args) {
String str = "ab";
int len = str.length();
int sum = 0;
//方法1
for (int i = 0; i < len; i++) {
//获取字符
char c = str.charAt(len - 1 - i);
System.out.println(c);
//获取对应的十进制数
int num = Character.digit(c, 16);
sum += num * (1 << 4 * i);
System.out.println("sum : " + sum);
}
//方法2
int num = Integer.parseInt(str, 16);
System.out.println("num : " + num);
}
集合的遍历方式
List有三种:普通for, 迭代器, 增强for(本质上也是迭代器)
Set有两种: 迭代器, 增强for
Map有两种: 获取键值对对象集合然后对set集合遍历, 获取键的set集合然后对集合遍历
import java.util.*;
public class Test {
public static void main(String[] args) {
/**
* 集合基本都重写了toString()方法,所以查看集合中的元素可以直接打印
*
* List 基于数组和链表,可重,存放有序(即放进去的顺序和出来的顺序是一致的),可存放null
* set 不可重(你写了多个重复的元素也只会出现一个),通过hashcode和equals判断是否重复
* 注意set存储自定义对象时,自定义类中需要重写这两个方法,不然就会出现可以存放重复元素的情况
* 因为不重写这两个方法就用object的,object中hashcode比较的是地址,两个对象的地址不可能一样
* hashset 底层是哈希表,存放无序(即放进去的顺序和出来的顺序不一定一致的),可存放null
* linkedhashset 底层是链表加哈希表 它存放有序且唯一,可存放null
* treeset 底层是红黑树,不能放null,通过比较器保证顺序,存放有序,比根节点小的都在左边,大的都在右边,采用中序遍历,先小后大
* map存储键值对,键唯一不重复,当有多个键重复时,取最晚加进来的键的value值
* hashmap,key和value都可以为null,用来替代hashtable的
* hashtable,key和value不可以为null
*
*/
//list遍历方式
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(null);
//list专用遍历方式:普通for
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
System.out.println();
System.out.println("-----------------");
//迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
System.out.println("-----------------");
//增强for
for (Integer i : list) {
System.out.print(i + " ");
}
System.out.println();
System.out.println("-----------------");
//set遍历方式,没有普通for
HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(1);
hashSet.add(2);
hashSet.add(3);
hashSet.add(3);
hashSet.add(null);
System.out.println(hashSet);
//迭代器
Iterator<Integer> setIterator = hashSet.iterator();
while (setIterator.hasNext()) {
System.out.print(setIterator.next() + " ");
}
System.out.println();
System.out.println("-----------------");
//增强for
for (Integer i : hashSet) {
System.out.print(i + " ");
}
System.out.println();
System.out.println("-----------------");
//map遍历方式
Map<String,Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
//1.获取所有键值对的集合,entry表示键值对对象,然后按照set方式遍历
Set<Map.Entry<String, Integer>> entries = map.entrySet();
//1.1增强for
for (Map.Entry<String, Integer> m : entries) {
System.out.print(m + " ");
}
System.out.println();
System.out.println("-----------------");
//1.2 迭代器
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
System.out.println("-----------------");
//2.根据键找值,然后根据set遍历方式遍历
//2.1 增强for
Set<String> strings = map.keySet();
for (String s : strings) {
System.out.print(s + map.get(s) + " ");
}
System.out.println();
System.out.println("-----------------");
//2.2 迭代器
Iterator<String> iterator1 = strings.iterator();
while (iterator1.hasNext()) {
String s = iterator1.next();
System.out.print(s + map.get(s)+ " ");
}
System.out.println();
System.out.println("-----------------");
}
}
Set接口,继承自Collection接口
Set特点:
1.元素不重复
1.哈希值:一个十进制的整数,没有重写hashCode()的话就由系统给出,反映对象的逻辑地址值(不是物理地址值,所以说哈希值相等不一定两个对象是同一个),通过object中的hashcode()方法可以得到
public native int hashCode();
其中native表示该方法调用的是本地操作系统的方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
tostring()方法中@后面跟的就是哈希值的十六进制形式
String重写了hashCode()方法,所以就根据其自己的方法来计算哈希值
String str = new String();
System.out.println(str.hashCode());//输出0
"重地".hashCode() == "通话".hashCode() == 1179395,这叫哈希冲突,即两个元素不同但是哈希值相同
这也再次证明了hashCode()值相等不代表两个对象相同
2.哈希表(查找速度快)相关
1.jdk1.8以前: 哈希表 = 数组 + 链表
2.jdk1.8以后: 哈希表 = 数组 + 链表,也等于数组 + 红黑树(为了提高查询速度,当链表长度超过8位,这时太大了就把链表转换为红黑树)
3.哈希表 = 数组 + 链表时: 数组中每个位置存放着哈希值相同的元素,在每个位置中,由链表将哈希值相同的元素连接起来,当链表长度大于8位了,就把链表转换成红黑树
set元素不存储重复元素的原理
set集合调用add()方法添加元素的时候,add()方法会调用元素的hashCode()方法和equals()方法判断元素是否重复(先hashCode()再equals()方法),不重复就放到数组中去.
要使得元素不重复,元素所属的类必须重写hashCode()方法和equals()方法.
2.没有索引(collection也没有索引),不能用所有跟索引有关的方法,也就不能用普通for
Set的实现类
HashSet
TreeSet
LinkedHashSet
HashSet
特点
元素不重复
没有索引
底层是哈希表(键值对结构,哈希表查询速度极快)
存放无序,即存储元素和取出元素的顺序有可能不一致
TreeSet(基于红黑树的NavigableMap实现)
1.特点:
1.能够对元素按照某种规则进行排序,可以使用元素的自然顺序进行排序,也可以根据创建set时提供的comparator接口进行排序,具体取决于使用的构造方法
2.和set一样,元素唯一
3.注意: 看treeset源码要去看treemap的,因为treeset是基于treemap实现的,直接看treeset的源码看不出来什么东西(写一个treeset然后按住ctrl+鼠标点一下就过去了)
4.treeset底层是红黑树(一种自平衡的二叉树),第二个元素从根节点开始比较,小就往左边放(return 负数),大就往右边放(return 正数),相等就不添加,一直比较到没有子节点,取的时候采用中序遍历
5.String,Integer都实现了comparable接口
2.排序(有两种方式):
1.自然排序(实现Comparable接口,重写compareTo方法)
1.例(自然排序,使用无参构造):
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<Integer>();
treeSet.add(1);
treeSet.add(0);
treeSet.add(-1);
treeSet.add(6);
Iterator<Integer> iterator = treeSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
输出: -1,0,1,6
2.上面的例子中,排序真正的比较是依赖于元素所在类的compareTo方法,而这个compareTo方法是定义在comparable接口中的,所以你要重写该方法就必须实现comparable接口,而Integer类中重写了该方法,所以可以对元素进行自然排序
2.比较器排序(实现Comparator接口)
1.例(比较器排序,让treeset创建对象时使用有参构造)
//TreeMap(Comparator<? super K> comparator)
继承comparator接口,实现其compare方法
public class MyComparator implements Comparator<Student>{
public int compare(Student o1, Student o2) {
//先判断年龄
int num = o1.age - o2.age;
//年龄相同再判断名字,名字为string类型,实现了自然排序接口
int num2 = num == 0? o1.name.compareTo(o2.name) : num;
return num2;
}
}
//测试类
public class Test {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<Student>(new MyComparator());
treeSet.add(new Student(12, "fan"));
treeSet.add(new Student(13, "li"));
treeSet.add(new Student(13, "lei"));
treeSet.add(new Student(14, "li"));
treeSet.add(new Student(14, "li"));
for (Student s:treeSet) {
System.out.println(s.name + ":" + s.age);
}
}
}
//学生类
public class Student {
public int age;
public String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
}
//当只用一次时候,用匿名内部类实现
public class Test {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {
public int compare(Student o1, Student o2) {
//先判断年龄
int num = o1.age - o2.age;
//年龄相同再判断名字,名字为string类型,实现了自然排序接口
int num2 = num == 0? o1.name.compareTo(o2.name) : num;
return num2;
}
});
treeSet.add(new Student(12, "fan"));
treeSet.add(new Student(13, "li"));
treeSet.add(new Student(13, "lei"));
treeSet.add(new Student(14, "li"));
treeSet.add(new Student(14, "li"));
for (Student s:treeSet) {
System.out.println(s.name + ":" + s.age);
}
}
}
3.treeset集合保证元素排序和唯一性原理:
1.唯一性: 是根据添加元素的过程中返回值是否为0决定的.
2.排序:
1.自然排序(集合使用无参构造, 元素具备比较性): 让元素所属的类实现comparable接口,并重写compareTo方法
2.比较器排序(集合使用有参构造, 集合具备比较性): 让treeset集合的构造方法接收一个comparator比较器接口的实现类对象,可以使用匿名类,也可以自己单独写一个类实现该接口,再重写其compare方法.
4.注意:比较器在哪里都可以用,只是以treeset为例子说明比较器的用法