文章目录
七、集合框架
ArrayList
和数组的区别
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
Person p1 = new Person("飞机");
Person p2 = new Person("火车");
//利用ArrayList,容量可变
ArrayList al = new ArrayList();
al.add(p1);
al.add(p2);
//输出和数组不同是因为,集合对象在toString()过程中会调用AbstractCollection的toString()塑形
//需要显示对象信息,就得重写toString()方法
System.out.println(al);
//利用数组,长度固定
Person[] pArray = new Person[2];
pArray[0] = p1;
pArray[1] = p2;
System.out.println(pArray);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
常用方法
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList al = new ArrayList();
//一、add()对象直接加在集合最后面
for (int i = 0; i < 5; i++) {
al.add(new Person("飞机"+i));
}
System.out.println(al);
//指定位置添加对象,指定位置不能超过集合现有容量
Person p1 = new Person("火车");
al.add(2,p1);
System.out.println(al);
//二、contains()判断一个对象是否在容器中,必须是同一个对象
System.out.println(al.contains(p1));
//三、get()获取指定位置的对象,如果输入的下标越界,会报错
System.out.println(al.get(3));
//四、indexof()获取对象所处的位置索引,如果没有该对象,则返回-1
System.out.println(al.indexOf(p1));
//五、remove()删除集合中的对象,参数可以是索引也可以是对象
al.remove(p1);
System.out.println(al);
//六、set()用于替换指定位置的元素为指定对象
al.set(2,p1);
System.out.println(al);
//七、size()获取集合的大小
System.out.println(al.size());
//八、toArray()将集合转化为数组,不指定类型转换的数组是Object类型
System.out.println(al.toArray());
//指定Person数组
Person[] pA = new Person[]{};
System.out.println(al.toArray(pA));
//九、addAll()将一个集合加入另一个集合中
ArrayList al2 = new ArrayList();
al2.add(new Person("汽车"));
al2.add(new Person("自行车"));
al.addAll(al2);
System.out.println(al);
al.addAll(1,al2);//指定位置添加
System.out.println(al);
al.addAll(al);//居然可以把自己添加到自己
System.out.println(al);
//十、clear()清空一个集合
al.clear();
System.out.println(al);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public void say(){
System.out.println("ha");
}
}
List接口
使用的时候用List来引用ArrayList对象,在后期需要对集合进行更改时,改动只需一行代码
List list = new ArrayList()
泛型初识
不指定泛型的容器,可以存放任何类型的元素
指定了泛型的容器,只能存放指定类型的元素以及其子类,取出不需要转型
遍历
import java.util.ArrayList;
import java.util.Iterator;
public class test {
public static void main(String[] args) {
ArrayList<Person> al = new ArrayList<>();
for (int i = 0; i < 5; i++) {
al.add(new Person("飞机"+i));
}
//第一种遍历for循环
for (int i = 0; i < al.size(); i++) {
System.out.println(al.get(i));
}
//迭代器遍历Iterator
Iterator<Person> it = al.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//增强for循环遍历
for (Person p : al) {
System.out.println(p);
}
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public void say(){
System.out.println("ha");
}
}
LinkedList
序列分先进先出FIFO,先进后出FILO,FIFO在Java中又叫Queue 队列,FIFO在Java中又叫Queue 队列
除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据
import java.util.LinkedList;
public class test {
public static void main(String[] args) {
LinkedList<Person> al = new LinkedList<>();
al.add(new Person("苹果"));
al.add(new Person("西瓜"));
al.add(new Person("葡萄"));
System.out.println(al);
//可以很方便的在前后插入元素
al.addFirst(new Person("第一个"));
al.addLast(new Person("后一个"));
System.out.println(al);
//可以方便查看最前与最后的元素
System.out.println(al.getFirst());
System.out.println(al.getLast());
//可以方便的移除最前最后元素
al.removeFirst();//返回取出的值
al.removeLast();//返回取出的值
System.out.println(al);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public void say(){
System.out.println("ha");
}
}
LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)
import java.util.LinkedList;
public class test {
public static void main(String[] args) {
LinkedList<Person> al = new LinkedList<>();
al.add(new Person("苹果"));
al.add(new Person("西瓜"));
al.add(new Person("葡萄"));
System.out.println(al);
//加在队列的最后面
al.offer(new Person("offer()"));
System.out.println(al);
//查看一个元素
System.out.println(al.peek());
System.out.println(al.peekFirst());
System.out.println(al.peekLast());
System.out.println(al);
//取出一个元素
al.poll();//返回取出的值
al.pollFirst();//返回取出的值
al.pollLast();//返回取出的值
System.out.println(al);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
public void say(){
System.out.println("ha");
}
}
二叉树
二叉树概念
二叉树由各种节点组成,二叉树特点:每个节点都可以有左子节点,右子节点 ,每一个节点都有一个值
二叉树排序-插入数据和排序
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args) {
int[] numb = {67, 7, 30, 73, 10, 0, 78, 81, 10, 74};
Node roots = new Node();
for (int in : numb) {
roots.add(in);
}
System.out.println(roots.inorder());
}
}
class Node{
public Node leftNode;
public Node rightNode;
public Object value;
public void add(int v){
if(null==value){
value = v;
}else{
if((Integer)v<=(Integer) value) {
if(leftNode == null){
leftNode = new Node();
leftNode.value = v;
}else {
leftNode.add(v);
}
}else {
if(rightNode == null){
rightNode = new Node();
rightNode.value = v;
}else {
rightNode.add(v);
}
}
}
}
public List inorder(){
List list = new ArrayList();
if(leftNode!=null){
list.addAll(leftNode.inorder());
}
// System.out.print(value+" ");
list.add(value);
if(rightNode!=null){
list.addAll(rightNode.inorder());
}
return list;
}
}
HashMap
HashMap储存数据的方式是—— 键值对
对于HashMap而言,key是唯一的,不可以重复的
所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。
import java.util.HashMap;
public class test {
public static void main(String[] args) {
HashMap<String, Integer> hm = new HashMap<String, Integer>();
hm.put("一",12);
hm.put("二",15);
hm.put("三",27);
System.out.println(hm);//从打印结果来看,HashMap插入是无序的
System.out.println(hm.get("三"));
}
}
HashSet
Set中的元素,不能重复,Set中的元素,没有顺序,严格的说,是没有按照元素的插入顺序排列
HashSet的具体顺序,既不是按照插入顺序,也不是按照hashcode的顺序。
Set不提供get()来获取指定位置的元素
import java.util.HashSet;
import java.util.Iterator;
public class test {
public static void main(String[] args) {
HashSet<Integer> hs = new HashSet<>();
hs.add(3);
hs.add(5);
hs.add(8);
hs.add(9);
System.out.println(hs);
//没有get()方法。只能使用迭代器和增强for循环来遍历
//使用迭代器
Iterator it = hs.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//for循环
for (Integer i : hs) {
System.out.println(i);
}
}
}
Collection
Collection是 Set、List、Queue(先进先出队列)和 Deque(双向链表)的接口
Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
Collections
Collections是一个类,容器的工具类,就如同Arrays是数组的工具类
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println("原集合:"+list);
Collections.reverse(list);//反转集合中的元素
System.out.println("反转后:"+list);
Collections.shuffle(list);//打乱集合中的元素
System.out.println("打乱后:"+list);
Collections.sort(list);//对集合中的元素进行排序
System.out.println("排序后:"+list);
Collections.swap(list,4,6);//交换两个元素
System.out.println("交换后:"+list);
Collections.rotate(list,3);//把List中的数据,向右滚动指定单位的长度
System.out.println("滚动后:"+list);
Collections.synchronizedList(list);//把非线程安全的List转换为线程安全的List
}
}
ArrayList和HashSet的区别
ArrayList: 有顺序,HashSet: 无顺序
List中的数据可以重复,Set中的数据不能够重复
重复判断标准是:hashcode相同且equals相同
ArrayList和Linkedlist的区别
ArrayList 插入,删除数据慢,LinkedList插入,删除数据快
ArrayList是顺序结构,所以定位很快,指哪找哪。 就像电影院位置一样,有了电影票,一下就找到位置了
LinkedList 是链表结构,就像手里的一串佛珠,要找出第99个佛珠,必须得一个一个的数过去,所以定位慢
HashMap和HashTable的区别
HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
HashMap可以存放 null
Hashtable不能存放null
区别2:
HashMap不是线程安全的类
Hashtable是线程安全的类
其他几种Set
HashSet: 无序,LinkedHashSet: 按照插入顺序,TreeSet: 从小到大排序
Hashcode原理
所有对象都对应一个Hashcode,Hashcode的值有相同的可能,
保存的时候不同的Hashcode放在指定的位置,相同的以链表的形式放在同一个位置
查找是依据指定位置查找,如果指定位置有多个就逐一比较(减少了比较次数)
这是一种用空间换时间的思维方式
比较器
当需要以对象中的属性大小来排列集合元素时,需要使用比较器,有两种方式来使用
第一种时使用Comparator
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class txt {
public static void main(String[] args){
List<Person> list = new ArrayList<>();
list.add(new Person("张三",15,1500));
list.add(new Person("王五",62,6500));
list.add(new Person("李四",30,900));
list.add(new Person("王八",2,7543));
list.add(new Person("刘七",56,1500));
System.out.println(list);
list.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if(o1.salary <= o2.salary){
return 0;//0和1都可以
}else {
return -1;
}
}
});
System.out.println(list);
}
}
class Person {
public String name;
public int age;
public int salary;
public Person(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
第二种是让对象实现Comparable接口,然后重写compareTo方法定义比较方式
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class txt {
public static void main(String[] args){
List<Person> list = new ArrayList<>();
list.add(new Person("张三",15,1500));
list.add(new Person("王五",62,6500));
list.add(new Person("李四",30,900));
list.add(new Person("王八",2,7543));
list.add(new Person("刘七",56,3452));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
class Person implements Comparable<Person>{
public String name;
public int age;
public int salary;
public Person(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
@Override
public int compareTo(Person p) {
if(this.age < p.age){
return 1;
}else{
return -1;
}
}
}
聚合操作
JDK8之后,引入了对集合的聚合操作,可以非常容易的遍历,筛选,比较集合中的元素。
但是要用好聚合,必须先掌握Lambda表达式,聚合的章节讲解放在Lambda与聚合操作部分详细讲解
练习
1)集合里查找名称等于指定字符串的对象
import java.util.ArrayList;
import java.util.List;
public class Exercise {
public static void main(String[] args){
List<Person> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new Person("名称"+i));
}
System.out.println(list);
System.out.println(isContain(list,"名称3"));
}
public static boolean isContain(List<Person> list,String name){
for (Person p : list) {
if(p.name.equals(name)){
return true;
}
}
return false;
}
}
class Person{
public String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
2)MyStringBuffer练习
做一个一样的MyStringBuffer练习,但是不使用字符数组,而是使用ArrayList来实现
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyStringBuffer implements InterfaceStringBuffer {
private List list;
public MyStringBuffer() {
list = new ArrayList();
}
public MyStringBuffer(String str) throws Exception {
list = new ArrayList();
append(str);
}
@Override
public void append(String str) throws Exception {
insert(list.size(),str);
}
@Override
public void append(char c) throws Exception {
insert(list.size(),c);
}
@Override
public void insert(int pos, char b) throws Exception {
if(pos<=list.size()){
list.add(pos,b);
}
}
@Override
public void insert(int pos, String str) throws Exception {
char[] ch = str.toCharArray();
for (int i = 0; i < ch.length; i++) {
insert(pos+i,ch[i]);
}
}
@Override
public void delete(int start) throws Exception {
if(start<list.size()){
list.remove(start);
}
}
@Override
public void delete(int start, int end) throws Exception {
if(end>start){
for (int i = 0; i < end-start+1; i++) {
delete(start);
}
}
}
@Override
public void reverse() {
// //相当与列表反转
Collections.reverse(list);
}
@Override
public int length() {
return list.size();
}
@Override
public String toString() {
String str="";
for (Object o : list) {
str = str + o;
}
return str;
}
public int capacity(){
return 0;
}
}
3)删除ArrayList中的数据
首先初始化一个Person集合,里面放100个Person对象,名称分别是从0-99,通过遍历的手段,删除掉名字编号是8的倍数的对象
import java.util.ArrayList;
import java.util.List;
public class Exercise {
public static void main(String[] args){
List<Person> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(new Person("名称 "+i));
}
System.out.println(list);//初始列表
List<Person> l = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
int index =Integer.parseInt(list.get(i).name.substring(3));
if(index%8 == 0){
l.add(list.get(i));
}
}
System.out.println(l);//可以被8整除的列表
list.removeAll(l);
System.out.println(list);//去除被8整除的列表
}
}
4)使用LinkedList实现Stack栈
与FIFO(先入先出的)队列类似的一种数据结构是FILO先入后出栈Stack
根据接口Stack ,实现类:MyStack,public class MyStack implements Stack
并向这个栈中,压入5个Person,接着弹出5个Person
Stack.java
public interface Stack {
//推入到最后位置
public void push(Person h);
//把最后一个取出来
public Person pull();
//查看最后一个
public Person peek();
}
MyStack.java
import java.util.LinkedList;
public class MyStack implements Stack{
public LinkedList<Person> llist;
public MyStack() {
llist = new LinkedList<>();
}
@Override
public void push(Person h) {
llist.push(h);
}
@Override
public Person pull() {
return llist.poll();
}
@Override
public Person peek() {
return llist.peek();
}
}
Exercise.java
public class Exercise {
public static void main(String[] args){
MyStack ms = new MyStack();
ms.push(new Person("阿一"));
ms.push(new Person("阿二"));
ms.push(new Person("阿三"));
ms.push(new Person("阿四"));
ms.push(new Person("阿五"));
System.out.println(ms.peek());
ms.pull();
System.out.println(ms.peek());
ms.pull();
System.out.println(ms.peek());
ms.pull();
System.out.println(ms.peek());
ms.pull();
System.out.println(ms.peek());
}
}
5)Person的二叉树练习
设计一个person二叉树,PersonNode,可以向这个二叉树里插入不同的Person对象,并按照Person的年龄排序
随机生成10个Person对象,每个Person对象都有不同的年龄,插入这个PersonNode后,把排序结果打印出来。
Exercise.java
import java.util.Random;
public class Exercise {
public static void main(String[] args){
PersonNode pn = new PersonNode();
Random rd = new Random();
for (int i = 0; i < 10; i++) {
pn.add(new Person("张三", rd.nextInt(120)));
}
inorder(pn);
}
public static void inorder(PersonNode p) {
if(p!=null){
if(p.getLeftNode() != null){
inorder(p.getLeftNode());
}
System.out.print(p.getP()+" ");
if(p.getRightNode() != null){
inorder(p.getRightNode());
}
}
}
}
PersonNode.java
public class PersonNode {
private PersonNode leftNode;
private PersonNode rightNode;
private Person p;
public void add(Person pe ){
if(this.p == null){
this.p = pe;
}else {
if(this.p.age > pe.age){
if(this.leftNode == null){
this.leftNode = new PersonNode();
}
this.leftNode.add(pe);
}else {
if(this.rightNode == null){
this.rightNode = new PersonNode();
}
this.rightNode.add(pe);
}
}
}
public PersonNode getLeftNode() {
return leftNode;
}
public PersonNode getRightNode() {
return rightNode;
}
public Person getP() {
return p;
}
}
6)排序的性能区别
比较冒泡法,选择法以及二叉树排序的性能区别
创建4万个随机数,然后用分别用冒泡法,选择法,二叉树3种排序算法进行排序,比较哪种更快
冒泡排序
import java.util.ArrayList;
import java.util.Random;
/***
* 二叉花费时间:47ms
* 选择花费时间:4312ms
* 冒泡花费时间:9718ms
*/
public class Exercise {
public static void main(String[] args){
//准备40000个随机数据
Random rd = new Random();
ArrayList<Integer> radm = new ArrayList<>();
for (int i = 0; i < 40000; i++) {
radm.add(rd.nextInt(100000));
}
//二叉树排序
long startTime = System.currentTimeMillis();
NumBinaryTree nbt = new NumBinaryTree();
for (int i = 0; i < 40000; i++) {
nbt.add(radm.get(i));
}
ArrayList<Integer> radmSort = new ArrayList<>();
// System.out.println("原数据:"+radm);
inorder(radmSort,nbt);
// System.out.println("二叉排序结果:"+radmSort);
long endTime = System.currentTimeMillis();
System.out.println("二叉花费时间:"+ (endTime - startTime) + "ms");
//选择排序
long startTime1 = System.currentTimeMillis();
selectionSort(radm);
long endTime1 = System.currentTimeMillis();
System.out.println("选择花费时间:"+ (endTime1 - startTime1) + "ms");
//冒泡排序
long startTime2 = System.currentTimeMillis();
bubbleSort(radm);
long endTime2 = System.currentTimeMillis();
System.out.println("冒泡花费时间:"+ (endTime2 - startTime2) + "ms");
}
public static void selectionSort(ArrayList<Integer> radm){
//选择排序时,每次循环内找出最小的都调换比较费时,而找出最小后循环外再做调换,可以节省时间
ArrayList<Integer> radmCopy = new ArrayList<>(radm);//为了不影响原列表,复制一个副本来操作
for (int i = 0; i < radmCopy.size()-1; i++) {
//循环外对调
int minIndex = i;
for (int j = i+1; j < radmCopy.size(); j++) {
if(radmCopy.get(minIndex)>radmCopy.get(j)) {
minIndex = j;
}
}
Integer temp = radmCopy.get(minIndex);
radmCopy.set(minIndex,radmCopy.get(i));
radmCopy.set(i,temp);
// //循环内对调
// for (int j = i+1; j < radmCopy.size(); j++) {
// if(radmCopy.get(i)>radmCopy.get(j)) {
// Integer temp = radmCopy.get(i);
// radmCopy.set(i,radmCopy.get(j));
// radmCopy.set(j,temp);
// }
// }
}
// System.out.println("选择排序结果:"+radmCopy);
}
public static void bubbleSort(ArrayList<Integer> radm){
ArrayList<Integer> radmCopy1 = new ArrayList<>(radm);//为了不影响原列表,复制一个副本来操作
for (int i = 0; i < radmCopy1.size()-1; i++) {
for (int j = 0; j < radmCopy1.size()-i-1; j++) {
if(radmCopy1.get(j) > radmCopy1.get(j+1)){
Integer temp = radmCopy1.get(j);
radmCopy1.set(j,radmCopy1.get(j+1));
radmCopy1.set(j+1,temp);
}
}
}
// System.out.println("冒泡排序结果:"+radmCopy1);
}
public static void inorder(ArrayList l,NumBinaryTree p) {
if(p!=null){
if(p.getLeftNode() != null){
inorder(l,p.getLeftNode());
}
// System.out.print(p.getValue()+" ");
l.add(p.getValue());
if(p.getRightNode() != null){
inorder(l,p.getRightNode());
}
}
}
}
NumBinaryTree.java
public class NumBinaryTree {
private NumBinaryTree leftNode;
private NumBinaryTree rightNode;
private Integer value;
public void add(Integer i){
if(value == null){
value = i;
}else {
if(i<value){
if (leftNode == null){
leftNode = new NumBinaryTree();
leftNode.value = i;
}else {
leftNode.add(i);
}
}else {
if (rightNode == null){
rightNode = new NumBinaryTree();
rightNode.value = i;
}else {
rightNode.add(i);
}
}
}
}
public NumBinaryTree getLeftNode() {
return leftNode;
}
public NumBinaryTree getRightNode() {
return rightNode;
}
public int getValue() {
return value;
}
}
7)查找内容性能比较
准备一个ArrayList其中存放3000000(三百万个)Person对象,其名称是随机的,格式是person-[4位随机数]
因为总数很大,所以几乎每种都有重复,把名字叫做 person-5555的所有对象找出来
要求使用两种办法来寻找
1、不使用HashMap,直接使用for循环找出来,并统计花费的时间
2、借助HashMap,找出结果,并统计花费的时间
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Exercise {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
for (int i = 0; i < 3000000;i++) {
list.add(new Person("person-"+((int)(Math.random()*9000)+1000)));
}
//for循环查询
long startTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < list.size(); i++) {
if(list.get(i).name.equals("person-5555")){
count++;
}
}
long endTime = System.currentTimeMillis();
System.out.println("找到名称为person-5555的数量:"+count);
System.out.println("for循环总共用时:"+(endTime-startTime)+"ms");
//hashmap查询
long startTime1 = System.currentTimeMillis();
HashMap<String, List<Person>> hm = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
if(!hm.containsKey(list.get(i).name)){//如果hashMap不包含这个名字的对象
hm.put(list.get(i).name,new ArrayList<>());//新建一个元素
}
hm.get(list.get(i).name).add(list.get(i));//获取这个key对应的值(列表),add这个对象
}
int result = 0;
if(hm.containsKey("person-5555")){
result = hm.get("person-5555").size();
}
long endTime1 = System.currentTimeMillis();
System.out.println("找到名称为person-5555的数量:"+result);
System.out.println("for循环总共用时:"+(endTime1-startTime1)+"ms");
}
}
8)比较字符串
创建一个长度是100的字符串数组,使用长度是2的随机字符填充该字符串数组,统计这个字符串数组里重复的字符串有多少种,使用HashSet来解决这个问题
import java.util.HashMap;
import java.util.HashSet;
public class Exercise {
public static void main(String[] args) {
String cons = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
String[] str = new String[100];
for (int i = 0; i < str.length; i++) {
str[i] = cons.charAt((int)(Math.random()*62))+""+cons.charAt((int)(Math.random()*62));
}
for (int i = 0; i < str.length; i++) {
if(i%10==0 && i!=0){
System.out.println();
}
System.out.print(str[i] +" ");
}
System.out.println();
HashSet<String> hs = new HashSet<>();
HashMap<String,Integer> hm = new HashMap<>();//存放重复的字符和个数
for (int i = 0; i < str.length; i++) {
if(!hs.add(str[i])){
if(hm.containsKey(str[i])){
hm.put(str[i], hm.get(str[i])+1);
}else{
hm.put(str[i], 2);
}
}
}
System.out.println("重复字符串及其个数:"+hm);
}
}
9)统计概率
首先初始化一个List,长度是10,值是0-9。然后不断的shuffle,直到前3位出现3 1 4,shuffle 1000,000 次,统计出现的概率
import java.util.*;
public class Exercise {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
int count= 0;
int number = 1000000;
for (int i = 0; i < number; i++) {
Collections.shuffle(list);
if(list.get(0)==3 && list.get(1)==1 && list.get(2)==4){
count++;
}
}
double probability = ((double)count/number);
System.out.println(count);
System.out.println("概率为:"+ probability*100+"%");
}
}
10)不重复的随机数
生成50个 0-9999之间的随机数,要求不能有重复的(利用set的不重复性)
import java.util.*;
public class Exercise {
public static void main(String[] args) {
HashSet<Integer> hs = new HashSet<>();
while (hs.size()<50){
hs.add((int)(Math.random()*9999));
}
System.out.println(hs.size());
}
}
11)插入数据性能比较
插入十万个数据,ArrayList和Linkedlist插入数据性能比较
import java.util.*;
/***
* ArrayList插入开头花费时间为:1484ms
* LinkedList插入开头花费时间为:16ms
* ArrayList插入中间花费时间为:2078ms
* LinkedList插入中间花费时间为:33466ms
* ArrayList插入末尾花费时间为:16ms
* LinkedList插入末尾花费时间为:0ms
*/
public class Exercise {
//linkedlist在中间插入的耗时与插入位置有关系,用add插入时如果位置比较靠前linkedlist会比较快
public static void main(String[] args) {
List<Integer> alist = new ArrayList<>();
List<Integer> llist = new LinkedList<>();
//初始化
for (int i = 0; i < 50; i++) {
alist.add(i);
llist.add(i);
}
test(alist,"ArrayList",0);
test(llist,"LinkedList",0);
test(alist,"ArrayList",1);
test(llist,"LinkedList",1);
test(alist,"ArrayList",2);
test(llist,"LinkedList",2);
}
public static void test(List<Integer> list,String type,int cas){
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
switch (cas){
case 0:
list.add(1, i);
break;
case 1:
list.add(list.size()/2, i);
break;
case 2:
list.add(i);
break;
}
}
long endTime1 = System.currentTimeMillis();
String[] str = {"开头","中间","末尾"};
System.out.println(type+"插入"+str[cas]+"花费时间为:"+ (endTime1-startTime1)+"ms");
}
}
12)定位数据比较
ArrayList和Linkedlist总长度都是100000,定位到50000的位置,取出数据并且+1,再放回去,重复100000遍,比较两个的效率。
import java.util.*;
/***
* ArrayList定位数据花费时间为:31ms
* LinkedList定位数据花费时间为:41656ms
*/
public class Exercise {
//linkedlist在中间插入的耗时与插入位置有关系,用add插入时如果位置比较靠前linkedlist会比较快
public static void main(String[] args) {
List<Integer> alist = new ArrayList<>();
List<Integer> llist = new LinkedList<>();
//初始化
for (int i = 0; i < 100000; i++) {
alist.add(i);
llist.add(i);
}
test(alist,"ArrayList");
test(llist,"LinkedList");
}
public static void test(List<Integer> list,String type){
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
list.set(50000,list.get(50000)+1);
}
long endTime1 = System.currentTimeMillis();
System.out.println(type+"定位数据花费时间为:"+ (endTime1-startTime1)+"ms");
}
}
13)反转key和value
使用键值对,初始化一个HashMap
对这个HashMap进行反转,key变成value,value变成key
提示: keySet()可以获取所有的key, values()可以获取所有的value
import java.util.HashMap;
import java.util.Iterator;
public class Exercise {
public static void main(String[] args) {
HashMap<String,String> hm = new HashMap<>();
HashMap<String,String> hm1 = new HashMap<>();//迭代过程中不能更改原map,所以新new一个
hm.put("张三","18");
hm.put("刘海","20");
hm.put("泰山","56");
hm.put("觅海","33");
Iterator<String> key = hm.keySet().iterator();
Iterator<String> values = hm.values().iterator();
System.out.println(hm);
while (key.hasNext()){
hm1.put(values.next(),key.next());
}
hm = hm1;
System.out.println(hm);
}
}
14)既不重复,又有顺序
利用LinkedHashSet的既不重复,又有顺序的特性,把Math.PI中的数字,按照出现顺序打印出来,相同数字,只出现一次
import java.util.LinkedHashSet;
public class Exercise {
public static void main(String[] args) {
String s = String.valueOf(Math.PI);
s = s.replace(".","");//去掉字符.字
LinkedHashSet<String> lhs = new LinkedHashSet<>();
for (int i = 0; i < s.length(); i++) {
lhs.add(s.substring(i,i+1));
}
for (String str : lhs) {
System.out.print(str + " ");
}
}
}
15)自定义字符串的hashcode
s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
s[0] 表示第一位字符
n表示字符串的长度
本练习并不是要求去理解这个算法,而是自定义一个简单的hashcode算法,计算任意字符串的hashcode
因为String类不能被重写,所以我们通过一个静态方法来返回一个String的hashcode
public static int hashcode(String)
如果字符串长度是0,则返回0。
否则: 获取每一位字符,转换成数字后,相加,最后乘以23
(s[0]+ s[1] + s[2] + s[3]+ s[n-1])*23.
如果值超过了1999,则取2000的余数,保证落在0-1999之间。
如果是负数,则取绝对值。
随机生成长度是2-10的不等的100个字符串,打印用本hashcode获取的值分别是多少
import java.util.Arrays;
public class Exercise {
public static void main(String[] args) {
//随机生成长度是2-10的不等的100个字符串
String cons = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
String[] str = new String[100];
Arrays.fill(str, "");
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < (int)(Math.random()*9)+2; j++) {
str[i] =str[i]+ cons.charAt((int)(Math.random()*62));
}
}
for (String s : str) {
System.out.format("%-10s的hashcode为:%d\n",s,hashcode(s));
}
}
public static int hashcode(String str){
int code = 0;
if(str.length() == 0){
return 0;
}else {
for (char c : str.toCharArray()) {
code = code+c;
}
code= code*23;
if(code>=2000){
code = code%2000;
}
if (code<0){
code = Math.abs(code);
}
return code;
}
}
}
16)自定义MyHashMap
根据前面学习的hashcode的原理和自定义hashcode, 设计一个MyHashMap,实现接口IHashMap
MyHashMap内部由一个长度是2000的对象数组实现。
设计put(String key,Object value)方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定 位到数组的指定位置。
如果该位置没有数据,则把字符串和对象组合成键值对Entry,再创建一个LinkedList,把键值对,放进 LinkedList中,最后把LinkedList 保存在这个位置。
如果该位置有数据,一定是一个LinkedList,则把字符串和对象组合成键值对Entry,插入到LinkedList后面。
设计 Object get(String key) 方法
首先通过上一个自定义字符串的hashcode练习获取到该字符串的hashcode,然后把这个hashcode作为下标,定 位到数组的指定位置。
如果这个位置没有数据,则返回空
如果这个位置有数据,则挨个比较其中键值对的键-字符串,是否equals,找到匹配的,把键值对的值,返回出 去。找不到匹配的,就返回空
Exercise.java
public class Exercise {
public static void main(String[] args) {
MyHashMap mhm = new MyHashMap();
//这两个key的hashcode一样,所以存在同一个列表里
mhm.put("y4",new Person("你好"));
mhm.put("z3",new Person("你好2"));
System.out.println(mhm.get("y4"));
}
}
IHashMap.java
public interface IHashMap {
public void put(String key,Object object);
public Object get(String key);
}
MyHashMap.java
import java.util.LinkedList;
public class MyHashMap implements IHashMap{
public LinkedList<Entry>[] ObjectArray = new LinkedList[2000];
@Override
public void put(String key, Object object) {
Entry entry = new Entry(key,object);
if(ObjectArray[hashcode(key)]==null){
LinkedList<Entry> linkedList = new LinkedList<>();
linkedList.add(entry);
ObjectArray[hashcode(key)] = linkedList;
}else {
ObjectArray[hashcode(key)].add(entry);
}
}
@Override
public Object get(String key) {
if (ObjectArray[hashcode(key)]==null){
return null;
}
for (Entry e : ObjectArray[hashcode(key)]) {
if(e.key.equals(key)) {
return e;
}
}
return null;
}
public static int hashcode(String str){
int code = 0;
if(str.length() == 0){
return 0;
}else {
for (char c : str.toCharArray()) {
code = code+c;
}
code= code*23;
if(code>=2000){
code = code%2000;
}
if (code<0){
code = Math.abs(code);
}
return code;
}
}
}
Entry.java
public class Entry {
public Object key;
public Object value;
public Entry(Object key, Object value) {
super();
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "[key=" + key + ", value=" + value + "]";
}
}
17)内容查找性能比较
重复前面的 练习-查找内容性能比较 ,不过不使用HashMap,而是使用上个练习中自定义的MyHashMap
import java.util.ArrayList;
import java.util.List;
/***
* MyHashMap的get方法返回的是一个Object类型
* 将这个类型转换为list类型费了一点功夫
*/
public class Exercise {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
for (int i = 0; i < 3000000;i++) {
list.add(new Person("person-"+((int)(Math.random()*9000)+1000)));
}
//for循环查询
long startTime = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < list.size(); i++) {
if(list.get(i).name.equals("person-5555")){
count++;
}
}
long endTime = System.currentTimeMillis();
System.out.println("找到名称为person-5555的数量:"+count);
System.out.println("for循环总共用时:"+(endTime-startTime)+"ms");
//hashmap查询
long startTime1 = System.currentTimeMillis();
MyHashMap hm = new MyHashMap();
for (Person person : list) {
List ls= (List) hm.get(person.name);
if (ls == null) {//如果hashMap不包含这个名字的对象
ls = new ArrayList<>();
hm.put(person.name, ls);//新建一个元素
}
ls.add(person);//获取这个key对应的值(列表),add这个对象
}
int result = 0;
List lss= (List) hm.get("person-5555");
if(lss != null){
result = lss.size();
}
long endTime1 = System.currentTimeMillis();
System.out.println("找到名称为person-5555的数量:"+result);
System.out.println("for循环总共用时:"+(endTime1-startTime1)+"ms");
}
}
18)自定义顺序的TreeSet
默认情况下,TreeSet中的数据是从小到大排序的,不过TreeSet的构造方法支持传入一个Comparator
通过这个构造方法创建一个TreeSet,使得其中的的数字是倒排序的
import java.util.Comparator;
import java.util.TreeSet;
public class Exercise {
public static void main(String[] args) {
Comparator cp = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if((Integer)o1 <(Integer)o2){
return 1;
}else {
return -1;
}
}
};
TreeSet<Integer> ts = new TreeSet<>(cp);
ts.add(25);
ts.add(10);
ts.add(64);
ts.add(23);
System.out.println(ts);
}
}