java实现赫夫曼树以及赫夫曼编码和解码(用byte[])

2023-11-15

首先对于赫夫曼编码有个大概的理解:赫夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。

赫夫曼编码,主要目的是根据使用频率来最大化节省字符(编码)的存储空间。(举例来说,对于一个字符串中"i like java do you like a java"中有多个重复字符,我们可以存储一次一个字符对应的编码即可,可以节省存储空间) ;网上大多数用的是char来进行字符个数的查询以及 编码和解码,其实赫夫曼编码初衷就是可以用来压缩图片等,具体在百度百科上有介绍,下文也有链接。如果用char[] 就限制到了字符串是没有体现出来的;此博客用byte数组来进行编码解码和存储;
哈夫曼编码大致过程:

  1. 将字符串转换为byte数组

  2. 检查byte数组中字符的出现次数,将字符的byte和字符出现次数保存为节点,放入集合中

  3. 构建哈夫曼树(最小带权二叉树)

  4. 设左路径为0 右路径为1 计算字符的哈夫曼编码值,存入Map集合中(以便我们用map集合来找变长编码对应的值,但是这种方式会造成一定的问题后边说)

  5. 参照Map集合,将byte数组转换为byte字符串,将其对照Map集合逐一处理,由于数据量较大,所以八位一组进行数据压缩
    步骤对应的动画演示为:
    在这里插入图片描述

对于各个步骤对应的代码加注释如下:

package binaryTree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.junit.Test;
/***
 * 
 * 没有实现文件压缩和解压,只需要对byte[]进行一下写入就可以了(最好用ObjectOutputStream 直接写入对象)
 * 这个实现byte类型转换成对应二进制string的方法不太好而且网上方法还没有这个好
 * @author 杰夫·王盖茨
 *
 */
public class HuffmanCode {
	private Map<Byte,String> huffManCodes ;
	private StringBuilder sb;
	public HuffmanCode() {
		huffManCodes = new HashMap<>();
		sb = new StringBuilder();
	}
	public static void main(String[] args) {
		String s = "i like like like java do you like a java";
		byte[] bytes = s.getBytes();
		HuffmanCode huffmanCode = new HuffmanCode();
		/*
		 * WeightNode root = huffmanCode.getRoot(huffmanCode.getWeightNode(bytes)); //
		 * root.preList(); huffmanCode.getHuffmanCode(root, "", huffmanCode.sb);
		 * Iterator<Entry<Byte, String>> iterator =
		 * huffmanCode.huffManCodes.entrySet().iterator(); while(iterator.hasNext()) {
		 * Entry<Byte, String> next = iterator.next(); System.out.println(next); }
		 * huffmanCode.gethuffmanCode(root); Iterator<Entry<Byte, String>> iterator1 =
		 * huffmanCode.huffManCodes.entrySet().iterator(); while(iterator.hasNext()) {
		 * Entry<Byte, String> next = iterator1.next(); System.out.println(next); }
		 */
		byte[] zip = huffmanCode.zip(bytes);
		System.out.println(Arrays.toString(zip));
		byte[] unZip = huffmanCode.unZip(zip, huffmanCode.huffManCodes);
		System.out.println(new String(unZip));
	}
	/**
	 * 将byte【】 统计重复byte之后得到对应的data和权重并且将他们包装成list
	 * @param bytes
	 * @return
	 */
	public List<WeightNode> getWeightNode(byte[] bytes) {
		HashMap<Byte, Integer> hashMap = new HashMap<>();
		for(byte b : bytes) {
			if(hashMap.get(b)==null) {
				hashMap.put(b, 1);
			}else {
				hashMap.replace(b, hashMap.get(b)+1);
			}
		}
		ArrayList<WeightNode> arrayList = new ArrayList<>();
		Iterator<Entry<Byte, Integer>> iterator = hashMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry<Byte, Integer> next = iterator.next();
			arrayList.add(new WeightNode(next.getKey(),next.getValue()));
		}
		return arrayList;
	}
	/**
	 * 将包装成的list生成一个赫夫曼树
	 * @param arrayList
	 * @return
	 */
	public WeightNode getRoot(List<WeightNode> arrayList) {
		while(arrayList.size() > 1) {
			Collections.sort(arrayList);

			WeightNode weightLeft = arrayList.get(0);
			WeightNode weightRight = arrayList.get(1);
			WeightNode parent = new WeightNode(weightLeft.weight+weightRight.weight);

			parent.left = weightLeft;
			parent.right = weightRight;

			arrayList.remove(weightRight);
			arrayList.remove(weightLeft);
			arrayList.add(parent);
		}
		return arrayList.get(0);
	}
	public Map<Byte,String> gethuffmanCode(WeightNode root){
		getHuffmanCode(root,"",sb);
		//这个sb还是""没有变
		System.out.println(sb.toString());
		System.out.println(huffManCodes);
		return huffManCodes;
	}
	/**
	 * 将给定结点下的所有叶子结点的对应的编码号作为string放入huffManCodes中
	 * @param node  结点
	 * @param code  是向左遍历还是向右遍历“0”是向左,“1”是向右
	 * @param sb  截止到上个结点处他的String
	 */
	public void getHuffmanCode(WeightNode node,String code,StringBuilder sb) {
		if(node == null) return;
		StringBuilder sb2 = new StringBuilder(sb);		
		sb2.append(code);
		if(node.data == null) {
			//向左递归
			getHuffmanCode(node.left,"0",sb2);
			//向右递归
			getHuffmanCode(node.right,"1",sb2);
		}else {
			huffManCodes.put(node.data, sb2.toString());
			System.out.print(sb2.toString());
		}
	}
	/**
	 * 给我一个最原始的byte数组我返回给你一个赫夫曼byte
	 * (这个byte是将原来byte排序后转换成相应的赫夫曼编码的数组之后再将八位一个转换成byte而形成的数组)
	 * @param bytes 最原始的byte数组
	 * @return 赫夫曼编码形成之后转byte的byte数组
	 */
	public byte[] zip(byte[] bytes) {
		StringBuilder strb = new StringBuilder();
		Map<Byte, String> gethuffmanCode = gethuffmanCode(getRoot(getWeightNode(bytes)));
		Iterator<Entry<Byte, String>> iterator = gethuffmanCode.entrySet().iterator(); 
		while(iterator.hasNext()) {
			Entry<Byte, String> next = iterator.next(); 
			System.out.println(next); 
		}
		for(byte b:bytes) {
			strb.append(gethuffmanCode.get(b));
		}
		int len = (strb.length()+7) / 8;
		byte[] huffManBytes = new byte[len];
		int index = 0;
		for(int i=0;i<strb.length();i += 8) {
			if(i+8<strb.length())
				huffManBytes[index] = (byte)Integer.parseInt(strb.substring(i, i+8),2);
			else huffManBytes[index] = (byte)Integer.parseInt(strb.substring(i),2);
			index++;
		}
		return huffManBytes;
	}
	/**
	 * 将一个byte 转成一个二进制的字符串
	 * @param flag 标志是否需要补高位如果是true ,表示需要补高位,如果是false表示不补, 如果是最后一个字节,无需补高位
	 * @param byteOne  传入的 byte
	 * @return 是该b 对应的二进制的字符串,(注意是按补码返回)
	 */
	public String huffManBinaryTonomarl(boolean flag,byte byteOne) {
		int temp = byteOne;
		String str;
		if(flag) {
			temp |= 256;
		}
		str = Integer.toBinaryString(temp);
		if (flag&&temp<0) {
			return str.substring(str.length() - 8);
		} else {
			return str;
		}
	}
	/**
	 * 给我一个最终存储形式的数组之后,我可以返回给你需要的做string 的byte数组;
	 * @param source 最终存储形式的byte数组
	 * @param huffManCodes 你的对应的赫夫曼编码集
	 * @return 解码后的byte数组,可以用来new string 来还原了;
	 */
	public byte[] unZip(byte[] source,Map<Byte,String> huffManCodes) {
		StringBuilder sb = new StringBuilder();
		//遍历数组中的每一个byte 得到用byte存储前的赫夫曼数组
		for(int i=0;i<source.length;i++) {
			byte b = source[i];
			if(i==source.length-1)
				sb.append(huffManBinaryTonomarl(false,b));
			else sb.append(huffManBinaryTonomarl(true,b));
		}
		
		//用一个新的map将之前的key和val反过来
		Map<String,Byte> result = new HashMap<>();
		for(Map.Entry<Byte, String> entry:huffManCodes.entrySet()) {
			result.put(entry.getValue(), entry.getKey());
		}
		
		//这是整个通过这赫夫曼的数组拿到编码成赫夫曼之前的数组
		Byte byteOne;
		boolean flag = true;
		ArrayList<Byte> arr = new ArrayList<>();
		System.out.println(sb.length());
		for(int i=0;i<sb.length();) {
			int count = 1;
			flag = true;
			while(flag) {
				byteOne = result.get(sb.substring(i,i+count));
				if(byteOne==null) {
					count++;
				}else {
					arr.add(byteOne);
					flag = false;
				}
			}
			i += count;
		}
		
		byte[] b = new byte[arr.size()];
		for(int i=0;i<arr.size();i++) {
			b[i] = arr.get(i);
		}
		return b;
	}
}
class WeightNode implements Comparable<WeightNode>{
	public Byte data;  //这个才是对应的数据
	public WeightNode left;
	public WeightNode right;
	public int weight;  //权重就是出现了几次
	public WeightNode(int weight) {
		this.weight = weight;
	}
	public WeightNode(byte data, int weight) {
		this.data = data;
		this.weight = weight;
	}
	public void preList() {
		System.out.println(this);
		if(this.left!=null) left.preList();
		if(this.right!=null) right.preList();
	}
	@Override
	public int compareTo(WeightNode o) {
		//使node可以排序,这边的逻辑如果换了对应的上边的get也是需要换的;
		return this.weight-o.weight;
	}
	@Override
	public String toString() {
		return "Node: ["+weight+"]";
	}
}

=============================================================
插句话:怎么打印二叉树结构类似这种在这里插入图片描述点击这里:https://blog.csdn.net/weixin_45127611/article/details/105668479

这里继续:
其中需要注意的:

  1. 解码的时候,最后一位byte转换成对应的二进制str的时候我们现有解决办法是不行的(有时候会解码错误),报错信息是对应二进制匹配不到我们已知的赫夫曼二进制str造成的索引越界异常,这个问题是韩顺平老师代码也不太完善而我也找不到解决办法(评论说的加if是不行的你可以多试验几次);总之这个问题我有自己的解决办法,网上对于赫夫曼编码大多数是char[] 的形式,没有对应于byte[]的解决办法;
  2. 赫夫曼编码出来的时候就是对应于图片了什么的压缩(有大量重复压缩效果才好),如果用char[] 就以为着我们只能对于字符串进行压缩,也就失去了他最主要的意义-压缩byte[]
  3. 上述代码没有写对于图片等其他格式文件的压缩但是只要你稍微改进(只用把byte[]源改了就可以了)就能够完成对任意文件的压缩;不过这种压缩是对于存在大量重复数据是效果最明显的,对于某些重复数据少的,压缩结果可能比原来文件还大;对于ppt这些文件的格式原本就是压缩过的,是不适用的可能会造成格式错误;
  4. 如果看过韩老师的课会发现上述代码是韩老师讲的,不过存在一定的问题,具体问题就是 1 中所描述的,我们通过例子来看一下具体错在哪个地方;
    错误举例: 当输入字符串为"i like like like java do you like a java you are lihai"时,程序会报错,错误提示为:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 186
	at java.lang.AbstractStringBuilder.substring(AbstractStringBuilder.java:933)
	at java.lang.StringBuilder.substring(StringBuilder.java:76)
	at binaryTree.HuffmanCode.unZip(HuffmanCode.java:211)
	at binaryTree.HuffmanCode.main(HuffmanCode.java:44)

原因我们一步一步分析: 按理说,我们给出的赫夫曼编码形式的(java中byte存储的是对应的补码)

000001000110001110110010111001101011011111001110101110111111
在我们的map中能够找到唯一的与之对应的key并且在循环的时候总是能够到最后一个位置完全匹配完成
32=01
97=100
100=111010
101=1111
104=111011
105=101
106=00111
107=1100
108=000
111=0010
114=00110
117=11010
118=11011
121=11100
对他进行编码 [-88, -71, -24, -71, -24, -71, -23, -26, -29, -47, 60, 45, 34, -25, -79, 60, -36, 120, 90, 97, -67, 23, -71, 1]

可是这里我们可以发现,当最后不足8个字符的时候我们进行编码例如01得到的是 1 再将1进行解码得到的结果也是 1 而正确的应该是 01,所以得到的结果跟我们想要的不一致;这样就找到了问题的来源,如果解决呢,这里我提供两个思路:

  1. 我们得到赫夫曼编码后是每八位再次进行byte编码的,最后一个可能是不足八位的,我们可以用一个变量记录下来到底是多少位,进如果过我们得到的最后位数不匹配就可以手动在其前边加"0";
  2. 我们不用Map映射的方式做,在赫夫曼编码的百度百科中,他对于此的实现(用的是C)就是根据你的二进制赫夫曼编码来对应着那颗最小权重二叉树找,如果对应不上就从头结点和下一个二进制数开始找,不过具体到这个办法我们需要保证:通过byte拿到对应的二进制编码的时候都需要是八位的 好比 1我们需要拿到00000001 然后根据树来找,对应的0000找不到就接着直到找到了01为止(可以参考百度百科上C++代码),还有一种拿到byte对应二进制的方法(结果都是八位):Integer.toBinaryString((byteOne & 0xFF) + 0x100).substring(1),如果看不懂可以参考文章:点击这里

但是我没有用java实现第二种方式,不知道有没有问题,下面贴出第一种解决办法的代码:

package binaryTree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.junit.Test;
/***
 * 这不是一个完整的赫夫曼编码(解码不太正确) 
 * 没有实现文件压缩和解压(主要是因为解码有时候会报错)
 * 这个实现byte类型转换成对应二进制string的方法不太好而且网上方法还没有这个好
 * @author 杰夫·王盖茨
 *
 */
public class HuffmanCode {
	private Map<Byte,String> huffManCodes ;
	private StringBuilder sb;
	private int leng; //用来记录如果最后一个byte不满八位具体是多少位
	public HuffmanCode() {
		huffManCodes = new HashMap<>();
		sb = new StringBuilder();
	}
	public static void main(String[] args) {
		String s = "i like like like java do you like a java you are lihai";
		byte[] bytes = s.getBytes();
		HuffmanCode huffmanCode = new HuffmanCode();
		/*
		 * WeightNode root = huffmanCode.getRoot(huffmanCode.getWeightNode(bytes)); //
		 * root.preList(); huffmanCode.getHuffmanCode(root, "", huffmanCode.sb);
		 * Iterator<Entry<Byte, String>> iterator =
		 * huffmanCode.huffManCodes.entrySet().iterator(); while(iterator.hasNext()) {
		 * Entry<Byte, String> next = iterator.next(); System.out.println(next); }
		 * huffmanCode.gethuffmanCode(root); Iterator<Entry<Byte, String>> iterator1 =
		 * huffmanCode.huffManCodes.entrySet().iterator(); while(iterator.hasNext()) {
		 * Entry<Byte, String> next = iterator1.next(); System.out.println(next); }
		 */
		byte[] zip = huffmanCode.zip(bytes);
		System.out.println(Arrays.toString(zip));
		byte[] unZip = huffmanCode.unZip(zip, huffmanCode.huffManCodes);
		System.out.println(new String(unZip));
	}
	/**
	 * 将byte【】 统计重复byte之后得到对应的data和权重并且将他们包装成list
	 * @param bytes
	 * @return
	 */
	public List<WeightNode> getWeightNode(byte[] bytes) {
		HashMap<Byte, Integer> hashMap = new HashMap<>();
		for(byte b : bytes) {
			if(hashMap.get(b)==null) {
				hashMap.put(b, 1);
			}else {
				hashMap.replace(b, hashMap.get(b)+1);
			}
		}
		ArrayList<WeightNode> arrayList = new ArrayList<>();
		Iterator<Entry<Byte, Integer>> iterator = hashMap.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry<Byte, Integer> next = iterator.next();
			arrayList.add(new WeightNode(next.getKey(),next.getValue()));
		}
		return arrayList;
	}
	/**
	 * 将包装成的list生成一个赫夫曼树
	 * @param arrayList
	 * @return
	 */
	public WeightNode getRoot(List<WeightNode> arrayList) {
		while(arrayList.size() > 1) {
			Collections.sort(arrayList);

			WeightNode weightLeft = arrayList.get(0);
			WeightNode weightRight = arrayList.get(1);
			WeightNode parent = new WeightNode(weightLeft.weight+weightRight.weight);

			parent.left = weightLeft;
			parent.right = weightRight;

			arrayList.remove(weightRight);
			arrayList.remove(weightLeft);
			arrayList.add(parent);
		}
		return arrayList.get(0);
	}
	public Map<Byte,String> gethuffmanCode(WeightNode root){
		getHuffmanCode(root,"",sb);
		//这个sb还是""没有变
		System.out.println(sb.toString());
		System.out.println(huffManCodes);
		return huffManCodes;
	}
	/**
	 * 将给定结点下的所有叶子结点的对应的编码号作为string放入huffManCodes中
	 * @param node  结点
	 * @param code  是向左遍历还是向右遍历“0”是向左,“1”是向右
	 * @param sb  截止到上个结点处他的String
	 */
	public void getHuffmanCode(WeightNode node,String code,StringBuilder sb) {
		if(node == null) return;
		StringBuilder sb2 = new StringBuilder(sb);		
		sb2.append(code);
		if(node.data == null) {
			//向左递归
			getHuffmanCode(node.left,"0",sb2);
			//向右递归
			getHuffmanCode(node.right,"1",sb2);
		}else {
			huffManCodes.put(node.data, sb2.toString());
			System.out.print(sb2.toString());
		}
	}
	/**
	 * 给我一个最原始的byte数组我返回给你一个赫夫曼byte
	 * (这个byte是将原来byte排序后转换成相应的赫夫曼编码的数组之后再将八位一个转换成byte而形成的数组)
	 * @param bytes 最原始的byte数组
	 * @return 赫夫曼编码形成之后转byte的byte数组
	 */
	public byte[] zip(byte[] bytes) {
		StringBuilder strb = new StringBuilder();
		Map<Byte, String> gethuffmanCode = gethuffmanCode(getRoot(getWeightNode(bytes)));
		Iterator<Entry<Byte, String>> iterator = gethuffmanCode.entrySet().iterator(); 
		while(iterator.hasNext()) {
			Entry<Byte, String> next = iterator.next(); 
			System.out.println(next); 
		}
		for(byte b:bytes) {
			strb.append(gethuffmanCode.get(b));
		}
		int len = (strb.length()+7) / 8;
		byte[] huffManBytes = new byte[len];
		int index = 0;
		leng = strb.length() % 8;
		for(int i=0;i<strb.length();i += 8) {
			if(i+8<strb.length())
				huffManBytes[index] = (byte)Integer.parseInt(strb.substring(i, i+8),2);
			else {System.out.println("这里"+i+strb.substring(i));huffManBytes[index] = (byte)Integer.parseInt(strb.substring(i),2);}
			index++;
		}
		return huffManBytes;
	}
	/**
	 * 将一个byte 转成一个二进制的字符串
	 * @param flag 标志是否需要补高位如果是true ,表示需要补高位,如果是false表示不补, 如果是最后一个字节,无需补高位
	 * @param byteOne  传入的 byte
	 * @return 是该b 对应的二进制的字符串,(注意是按补码返回)
	 */
	public String huffManBinaryTonomarl(boolean flag,byte byteOne) {
		int temp = byteOne;
		String str;
		if(flag) {
			temp |= 256;
		}
		str = Integer.toBinaryString(temp);
		if(temp<0) return str.substring(str.length() - 8);
		if (flag) {
			return str.substring(str.length() - 8);
		} else {
			while(str.length()<leng) {
				str = "0" + str;
			}
			return str;
		}
	}
	/**
	 * 给我一个最终存储形式的数组之后,我可以返回给你需要的做string 的byte数组;
	 * @param source 最终存储形式的byte数组
	 * @param huffManCodes 你的对应的赫夫曼编码集
	 * @return 解码后的byte数组,可以用来new string 来还原了;
	 */
	public byte[] unZip(byte[] source,Map<Byte,String> huffManCodes) {
		StringBuilder sb = new StringBuilder();
		//遍历数组中的每一个byte 得到用byte存储前的赫夫曼数组
		for(int i=0;i<source.length;i++) {
			byte b = source[i];
			if(i==source.length-1)
				sb.append(huffManBinaryTonomarl(false,b));
			else sb.append(huffManBinaryTonomarl(true,b));
		}
		
		//用一个新的map将之前的key和val反过来
		Map<String,Byte> result = new HashMap<>();
		for(Map.Entry<Byte, String> entry:huffManCodes.entrySet()) {
			result.put(entry.getValue(), entry.getKey());
		}
		
		//这是整个通过这赫夫曼的数组拿到编码成赫夫曼之前的数组
		Byte byteOne;
		boolean flag = true;
		ArrayList<Byte> arr = new ArrayList<>();
		System.out.println(sb.length());
		for(int i=0;i<sb.length();) {
			int count = 1;
			flag = true;
			while(flag) {
				byteOne = result.get(sb.substring(i,i+count));
				if(byteOne==null) {
					count++;
				}else {
					arr.add(byteOne);
					flag = false;
				}
			}
			i += count;
		}
		
		byte[] b = new byte[arr.size()];
		for(int i=0;i<arr.size();i++) {
			b[i] = arr.get(i);
		}
		return b;
	}
}
class WeightNode implements Comparable<WeightNode>{
	public Byte data;  //这个才是对应的数据
	public WeightNode left;
	public WeightNode right;
	public int weight;  //权重就是出现了几次
	public WeightNode(int weight) {
		this.weight = weight;
	}
	public WeightNode(byte data, int weight) {
		this.data = data;
		this.weight = weight;
	}
	public void preList() {
		System.out.println(this);
		if(this.left!=null) left.preList();
		if(this.right!=null) right.preList();
	}
	@Override
	public int compareTo(WeightNode o) {
		//使node可以排序,这边的逻辑如果换了对应的上边的get也是需要换的;
		return this.weight-o.weight;
	}
	@Override
	public String toString() {
		return "Node: ["+weight+"]";
	}
}

解决方式二对应的动画演示,快速版
在这里插入图片描述
制作不易,欢迎点赞

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java实现赫夫曼树以及赫夫曼编码和解码(用byte[]) 的相关文章

  • Spring Security 通过并发登录尝试将用户锁定

    我是安全新手 遇到了一个问题 该问题导致用户帐户被锁定 只有重新启动应用程序才能修复它 我有一个带有 spring security 4 0 2 RELEASE 应用程序的 spring boot 1 3 0 BUILD SNAPSHOT
  • 实现与扩展:何时使用?有什么不同?

    请用易于理解的语言进行解释或提供某些文章的链接 extends is for 延伸一类 implements is for 实施一个接口 接口和常规类之间的区别在于 在接口中您不能实现任何声明的方法 只有 实现 接口的类才能实现方法 C 中
  • 在Java中清空数组/处理

    除了循环遍历数组中的每个元素并将每个元素设置为 null 之外 Java 处理中是否有一个本机函数可以简单地清空数组 或销毁它 以便能够将其重新声明为新数组 There s Arrays fill myArray null 并不是说它执行的
  • firebase推送通知错误Spring Boot服务器端

    我正在尝试从 Spring Boot 服务器端发送通知到客户端 android 服务器运行良好 一切都很好 2020 09 01 08 13 07 691 INFO 18941 restartedMain e DevToolsPropert
  • 正确使用 JDBC 连接池 (Glassfish)

    我需要在 Java Web 服务中作为会话 bean 实现数据库连接 但我不确定我这样做是否正确 我创建了一个类 public final class SQLUtils private static DataSource m ds null
  • 如何修复 Android 7.0 的 Spinner 模式下的 DatePickerDialog?

    我目前正在开发一个简单的项目 其中包含一个包含在 Web 视图中的网站 具有少量交互 以提高网站本身和 Android 移动设备之间的交互性 由于该网站包含用户生日的日期输入字段 因此我希望实现一个与所有设备兼容的旋转格式的日期选择器 我尝
  • 用 java 编写解释器时的 switch 或 if 语句

    当前的作业需要我编写一个程序 以一种非常微小且基本的编程语言 行为有点像 FORTRAN 来读取包含指令的文件并执行这些指令 基本上它是我猜的语言的简单解释器 它是完全线性的 所有语句都是按顺序定义的 并且只有字符串和整数变量 我需要查找和
  • 支持通过 OAuth 进行 Facebook/Twitter 身份验证的 CAS 服务器

    我正在寻找一个支持 Facebook Twitter 通过 OAuth 进行单点登录身份验证的 CAS 服务器 我检查过 JASIG CAS 服务器 但它看起来不支持它们 我的 java web 应用程序基于 Spring Security
  • 以编程方式设置 Logback Appender 路径

    我正在尝试以编程方式设置 Logback 附加程序路径 滚动文件附加器 http logback qos ch apidocs ch qos logback core rolling RollingFileAppender html准确地说
  • C# 中的协变和逆变

    首先我要说的是 我是一名正在学习 C 编程的 Java 开发人员 因此 我会将我所知道的与我正在学习的进行比较 我已经使用 C 泛型几个小时了 我已经能够在 C 中重现我在 Java 中知道的相同内容 除了几个使用协变和逆变的示例 我正在读
  • Java:使用 Java.util.concurrent 线程访问读取线程串行端口

    我正在尝试编写一个 Java 串行设备驱动程序并想使用 对我来说是新的 java util concurrent包裹 我有一种发送数据包然后等待 ACK 的方法 我打算有炭 接收在不同的线程中运行 如果接收线程收到 ACK 它应该使用发送数
  • 如何使用 Guava 连接字符串?

    我写了一些代码来连接字符串 String inputFile for String inputLine list inputFile inputLine trim 但我不能使用 连接 所以我决定使用 Guava 所以我需要使用Joiner
  • 使用Java开发跨平台,不同平台字体缩放不同

    我正在为我的大学制作一些软件 需要一个 GUI 在它的第一个版本中 我让它使用系统外观 因此它看起来像 Linux Mac Windows 中的本机应用程序 我发现这很麻烦 因为我必须根据操作系统使所有 JLabel 具有不同的大小 无论分
  • 如何使用云打印打印Android活动显示

    我正在尝试将 Google 云打印实现到应用程序中 遵循集成指南 https developers google com cloud print docs android 我试图通过打印 google com 来保持基本 单击我创建的打印按
  • 在服务器内部调用 Web 服务

    我有一个网络服务 getEmployee 当传递 id 时 它会获取单个员工的员工详细信息 同一服务器上的另一个 Web 服务 getEmployeeList 当传递一个部门时 它会获取整个员工列表 这将获取部门的 ID 然后调用 getE
  • Google Cloud Messaging - 立即收到或长时间延迟收到的消息

    我在大学最后一年的项目中使用谷歌云消息传递 一切正常 但我在使用 GCM 时遇到了一些麻烦 通常 消息要么几乎立即传递 要么有很大的延迟 我读过这篇文章 但我真的认为它不适用于这种情况 GCM 通常会在消息发送后立即传送消息 然而 这并不总
  • 编写自定义 Eclipse 调试器

    EDIT 一定有某种方法可以解决这个问题 而无需编写全新的调试器 我目前正在研究在现有 java 调试器之上构建的方法 如果有人对如何获取 Java 调试器已有的信息 有关堆栈帧 变量 原始数据等 有任何想法 那将非常有帮助 我想要做的是我
  • H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()

    所以我有一个 DB2 生产数据库 我需要在其中使用可用的函数 ListAgg 我希望使用 H2 的单元测试能够正确测试此功能 不幸的是H2不直接支持ListAgg 但是 我可以创建一个用户定义的聚合函数 import java sql Co
  • Integer.parseInt 引发的 NumberFormatException

    嘿 我在学校上编码课 但老师没有很好地解释 所以我们必须在网上查找我所做的信息 但我无法找到代码中的错误 你能帮我吗 char end s do System out println Tipo de boleto char boleto c
  • Libgdx 和 Google 应用内购买结果

    我遵循了这些指示 https github com libgdx libgdx wiki Interfacing with platform specific code使用 ActionResolver 接口集成 Libgdx 和原生 An

随机推荐

  • 利用逆矩阵解线性方程组_经典Jacobi方法用于求解矩阵特征值

    1 引言 求解线性方程组在许多领域中都有重要应用 写成矩阵的形式 求解 可以写成 这里需要求解矩阵 的逆 线性代数 中给出的方法主要有两类 1 设置增广矩阵 利用高斯消元法 通过初等行列变换可以求 但这种方法不利于使用计算机计算 2 利用矩
  • Bootstarp入门教程(2) 概述

    概述 深入了解Bootstrap底层结构的关键部分 包括我们让web开发变得更好 更快 更强壮的最佳实践 1 HTML5文档类型 Bootstrap使用到的某些HTML元素和CSS属性需要将页面设置为HTML5文档类型
  • 【git】git compare with branch 一样的代码 但是却标识不一样 成块显示 Git 比较 不准确

    文章目录 1 概述 本文地址 https blog csdn net qq 21383435 article details 119483593 1 概述 我一个flink项目做git比较 用flink 1 9版本的对比1 13版本的 发现
  • Go_配置系统环境MacOS(M1)

    在MacOS下和JDK一样 配不配环境其实MacOS都是可以检测的到的 安装好以后直接输入go version是一样可以的 因为都是使用开发工具的 在开发工具里配置的话是样的 如果有习惯的话就配置一下吧 下载安装及配置环境 1 官网下载 h
  • 基于QT开发的跨平台文件校验工具

    QtFileHash QtFileHash是一款基于Qt开发的跨平台文件校验工具 支持Windows Linux MacOS平台 支持MD4 MD5 SHA1 SHA256 SHA512算法 项目地址 https github com zd
  • Linux下共享文件夹的位置在哪

    大家都知道vm这个虚拟机有个共享文件夹的功能 怎么设置这个共享文件夹在图示位置 但问题是在虚拟机里面这个文件夹的位置在哪里呢 我上网找了半天也没有解决方案 这里直接告诉大家 应该是被隐藏了但是我用 ls al看也没有 直接在用户 unbun
  • python 图片的读取、显示、处理与保存(PIL和OpenCV)

    目录 0 前言 1 图片读取 显示和保存 2 PIL与cv2相互转换 3 处理与保存 3 1 裁剪 3 2 绘制矩形 参考链接 0 前言 先撇开matplotlib不谈 在python江湖用于读取图片的主要为两个门派 分别是PIL家族 fr
  • Object.is()

    ES5 比较两个值是否相等 只有两个运算符 相等运算符 两个 和严格相等运算符 三个 它们都有缺点 前者会自动转换数据类型 后者的NaN不等于自身 以及 0等于 0 JavaScript 缺乏一种运算 在所有环境中 只要两个值是一样的 它们
  • 社区版VS2019下配置Opencv4.5.3

    1 下载Opencv 我下的是4 5 3 下载地址 https sourceforge net projects opencvlibrary 解压安装 没记错的话是 exe 文件 双击安装 没啥要注意的 选好路径就行 安装完成 安装完成后是
  • STM32——OLED调试工具与显示屏

    文章目录 一 调试工具 二 OLED简介 三 硬件电路接线图 四 OLED驱动函数 五 源码 OLED c OLED h OLED Font h 一 调试工具 调试方式 串口调试 通过串口通信 将调试信息发送到电脑端 电脑使用串口助手显示调
  • 外盘国际期货

    什么是交易 有人说交易就是买卖 从字面看这没啥毛病 概括性也很强 但我们不妨再细细掰扯下 交易 的深层含义 我们可以先将 交易 二字拆开来逐字理解与分析其意 交 象形字 一个人 最早出现在甲骨文 本义 动词 反叉两腿站立 说文解字 里泛指交
  • Docker修改已有镜像,并打包生成新的镜像tar文件

    文章目录 一 加载镜像 二 运行镜像 三 对镜像进行修改 四 将容器打包成新的镜像 五 将新的镜像保存为tar包 一 加载镜像 在镜像目录下打开linux终端 输入如下命令 docker load i 镜像文件名 tar 二 运行镜像 镜像
  • shineblink HC-SR505人体红外感应传感器

    HC SR505人体红外感应传感器 一 本例程实现功能 二 HC SR505传感器介绍 三 接线图 四 完整代码 五 代码运行结果 一 本例程实现功能 通过HC SR505人体红外感应传感器模块感应人体的出现 当感应到人体时 Core电路板
  • 支持二级汉字的 php 汉字助记码生成

    gbk2312 编码范围共94区 0 55区为一级汉字 是按照拼音顺序排列的 可以按照编码区间确定汉字的拼音 但是 56 区以后是按笔画顺序排列的 所以只能用对照表来确定拼音 鉴于目前我找不到现成的代码 固整理了一份 测试可用 PHP 汉字
  • 【操作系统】王道考研 p11 线程概念、多线程模型

    视频 知识总览 是什么 为什么 进程是程序的一次执行 但有些功能不是由一个程序顺序处理就能实现的 有的进程可能需要 同时 做很多事情 而传统的进程只能串行地执行一系列程序 因此 引入 线程 来增加并发度 引入线程后 线程成为了程序执行流的最
  • esp8266连接mqtt时client.publish出现的小问题

    client publish发送变化数据的问题 温湿度为例 先是获取数据 加入我们获取的湿度为humidity 温度为temp 下一步就是我们的发送环节啦 client publish 主题 内容 我们字节将内容替换是会报错的 我们需要对数
  • qemu-guest-agent windows下的监控开发

    windows下的qemu guest agent 简称qga 的名字叫做qemu guest agent win32 目前最新版的版本号是qemu guest agent win32 0 12 1 2 2 355 el6 4 9 x86
  • 【踩坑】三种方式解决 Homebrew failing to install - fatal: not in a git directory

    问题描述 解决方法一 添加安全目录 没有测试 git config global add safe directory opt homebrew Library Taps homebrew homebrew git config globa
  • 动手写docker中遇到的问题合集

    fork exec usr bin sh operation not permitted exit status 1 我的环境 win10 wsl2 ubuntu20 04 解决方案 设置wsl默认用户为root sudo nano etc
  • java实现赫夫曼树以及赫夫曼编码和解码(用byte[])

    首先对于赫夫曼编码有个大概的理解 赫夫曼编码 Huffman Coding 又称霍夫曼编码 是一种编码方式 可变字长编码 VLC 的一种 Huffman于1952年提出一种编码方法 该方法完全依据字符出现概率来构造异字头的平均长度最短的码字