这是indexloc提供的服务,不要输入任何密码
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package com.baeldung.apache.avro;

import org.apache.avro.Conversion;
import org.apache.avro.LogicalTypes;
import org.apache.avro.Schema;
import org.apache.avro.data.TimeConversions;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.Encoder;
import org.apache.avro.io.EncoderFactory;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class SerializeAndDeserializeDateUnitTest {

@Test
void whenSerializingDateWithLogicalType_thenDeserializesCorrectly() throws IOException {

LocalDate expectedDate = LocalDate.now();
Instant expectedTimestamp = Instant.now();

byte[] serialized = serializeDateWithLogicalType(expectedDate, expectedTimestamp);
Pair<LocalDate, Instant> deserialized = deserializeDateWithLogicalType(serialized);

assertEquals(expectedDate, deserialized.getLeft());

// This is perfectly valid when using logical types
assertEquals(expectedTimestamp.toEpochMilli(), deserialized.getRight().toEpochMilli(),
"Timestamps should match exactly at millisecond precision");
}

@Test
void whenSerializingWithConversionApi_thenDeserializesCorrectly() throws IOException {

LocalDate expectedDate = LocalDate.now();
Instant expectedTimestamp = Instant.now();

byte[] serialized = serializeWithConversionApi(expectedDate, expectedTimestamp);
Pair<LocalDate, Instant> deserialized = deserializeWithConversionApi(serialized);

assertEquals(expectedDate, deserialized.getLeft());
assertEquals(expectedTimestamp.toEpochMilli(), deserialized.getRight().toEpochMilli(),
"Timestamps should match at millisecond precision");
}

@Test
void whenSerializingLegacyDate_thenConvertsCorrectly() throws IOException {

Date legacyDate = new Date();
LocalDate expectedLocalDate = legacyDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();

byte[] serialized = serializeLegacyDateAsModern(legacyDate);
LocalDate deserialized = deserializeDateWithLogicalType(serialized).getKey();

assertEquals(expectedLocalDate, deserialized);
}

public static Schema createDateSchema() {
String schemaJson =
"{"
+ "\"type\": \"record\","
+ "\"name\": \"DateRecord\","
+ "\"fields\": ["
+ " {\"name\": \"date\", \"type\": {\"type\": \"int\", \"logicalType\": \"date\"}},"
+ " {\"name\": \"timestamp\", \"type\": {\"type\": \"long\", \"logicalType\": \"timestamp-millis\"}}"
+ "]"
+ "}";
return new Schema.Parser().parse(schemaJson);
}

public static byte[] serializeDateWithLogicalType(LocalDate date, Instant timestamp) throws IOException {
Schema schema = createDateSchema();
GenericRecord record = new GenericData.Record(schema);

// Convert LocalDate to days since epoch
record.put("date", (int) date.toEpochDay());

// Convert Instant to milliseconds since epoch
record.put("timestamp", timestamp.toEpochMilli());

ByteArrayOutputStream baos = new ByteArrayOutputStream();
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(schema);
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);

datumWriter.write(record, encoder);
encoder.flush();

return baos.toByteArray();
}

public static Pair<LocalDate, Instant> deserializeDateWithLogicalType(byte[] bytes) throws IOException {
Schema schema = createDateSchema();
DatumReader<GenericRecord> datumReader = new GenericDatumReader<>(schema);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);

GenericRecord record = datumReader.read(null, decoder);

// Convert days since epoch back to LocalDate
LocalDate date = LocalDate.ofEpochDay((int) record.get("date"));

// Convert milliseconds since epoch back to Instant
Instant timestamp = Instant.ofEpochMilli((long) record.get("timestamp"));

return Pair.of(date, timestamp);
}

public static byte[] serializeWithConversionApi(LocalDate date, Instant timestamp) throws IOException {
Schema schema = createDateSchema();
GenericRecord record = new GenericData.Record(schema);

// Use LogicalTypes.date() for conversion
Conversion<LocalDate> dateConversion = new org.apache.avro.data.TimeConversions.DateConversion();
LogicalTypes.date().addToSchema(schema.getField("date").schema());

// Use LogicalTypes.timestampMillis() for conversion
Conversion<Instant> timestampConversion = new org.apache.avro.data.TimeConversions.TimestampMillisConversion();
LogicalTypes.timestampMillis().addToSchema(schema.getField("timestamp").schema());

record.put("date", dateConversion.toInt(date, schema.getField("date").schema(), LogicalTypes.date()));
record.put("timestamp", timestampConversion.toLong(timestamp, schema.getField("timestamp").schema(), LogicalTypes.timestampMillis()));

// Serialize as before
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(schema);
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);

datumWriter.write(record, encoder);
encoder.flush();

return baos.toByteArray();
}

public static Pair<LocalDate, Instant> deserializeWithConversionApi(byte[] bytes) throws IOException {
Schema schema = createDateSchema();
DatumReader<GenericRecord> datumReader = new GenericDatumReader<>(schema);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);

GenericRecord record = datumReader.read(null, decoder);

// Use LogicalTypes.date() for conversion
Conversion<LocalDate> dateConversion = new TimeConversions.DateConversion();
LogicalTypes.date().addToSchema(schema.getField("date").schema());

// Use LogicalTypes.timestampMillis() for conversion
Conversion<Instant> timestampConversion = new TimeConversions.TimestampMillisConversion();
LogicalTypes.timestampMillis().addToSchema(schema.getField("timestamp").schema());

// Get the primitive values from the record
int daysSinceEpoch = (int) record.get("date");
long millisSinceEpoch = (long) record.get("timestamp");

// Convert back to Java types using the conversion API
LocalDate date = dateConversion.fromInt(
daysSinceEpoch,
schema.getField("date").schema(),
LogicalTypes.date()
);

Instant timestamp = timestampConversion.fromLong(
millisSinceEpoch,
schema.getField("timestamp").schema(),
LogicalTypes.timestampMillis()
);

return Pair.of(date, timestamp);
}

public static byte[] serializeLegacyDateAsModern(Date legacyDate) throws IOException {
// Convert java.util.Date to java.time.Instant
Instant instant = legacyDate.toInstant();

// Convert to LocalDate if you need date-only information
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();

// Then use one of our modern date serialization methods
return serializeDateWithLogicalType(localDate, instant);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.baeldung.backtracking;

import java.util.ArrayList;
import java.util.List;

public class Backtracking {

private Backtracking() {
}

public static List<String> process(String digits, int target) {
final List<String> validExpressions = new ArrayList<>();
evaluateExpressions(validExpressions, new Equation(digits, target), 0, new StringBuilder(), 0, 0);
return validExpressions;
}

private static void evaluateExpressions(List<String> validExpressions, Equation equation, int index, StringBuilder currentExpression, long currentResult,
long lastOperand) {

if (allDigitsProcessed(equation.getDigits(), index)) {
if (currentResult == equation.getTarget()) {
validExpressions.add(currentExpression.toString());
}
return;
}

exploreExpressions(validExpressions, equation, index, currentExpression, currentResult, lastOperand);
}

private static boolean allDigitsProcessed(String digits, int index) {
return index == digits.length();
}

private static void exploreExpressions(List<String> validExpressions, Equation equation, int index, StringBuilder currentExpression, long currentResult,
long lastOperand) {

for (int endIndex = index; endIndex < equation.getDigits()
.length(); endIndex++) {
if (isWithLeadingZero(equation.getDigits(), index, endIndex)) {
break;
}

long currentOperandValue = Long.parseLong(equation.getDigits()
.substring(index, endIndex + 1));

if (isFirstOperand(index)) {
processFirstOperand(validExpressions, equation, endIndex, currentExpression, currentOperandValue);
} else {
applyAddition(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue);
applySubtraction(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue);
applyMultiplication(validExpressions, equation, endIndex, currentExpression, currentResult, currentOperandValue, lastOperand);
}
}
}

private static boolean isWithLeadingZero(String digits, int index, int endIndex) {
return endIndex > index && digits.charAt(index) == '0';
}

private static boolean isFirstOperand(int index) {
return index == 0;
}

private static void processFirstOperand(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression,
long currentOperandValue) {
appendToExpression(currentExpression, Operator.NONE, currentOperandValue);

evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentOperandValue, currentOperandValue);

removeFromExpression(currentExpression, Operator.NONE, currentOperandValue);
}

private static void applyAddition(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
long currentOperandValue) {
appendToExpression(currentExpression, Operator.ADDITION, currentOperandValue);

evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult + currentOperandValue, currentOperandValue);

removeFromExpression(currentExpression, Operator.ADDITION, currentOperandValue);
}

private static void applySubtraction(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
long currentOperandValue) {
appendToExpression(currentExpression, Operator.SUBTRACTION, currentOperandValue);

evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult - currentOperandValue, -currentOperandValue);

removeFromExpression(currentExpression, Operator.SUBTRACTION, currentOperandValue);
}

private static void applyMultiplication(List<String> validExpressions, Equation equation, int endIndex, StringBuilder currentExpression, long currentResult,
long currentOperandValue, long lastOperand) {
appendToExpression(currentExpression, Operator.MULTIPLICATION, currentOperandValue);

evaluateExpressions(validExpressions, equation, endIndex + 1, currentExpression, currentResult - lastOperand + (lastOperand * currentOperandValue),
lastOperand * currentOperandValue);

removeFromExpression(currentExpression, Operator.MULTIPLICATION, currentOperandValue);
}

private static void appendToExpression(StringBuilder currentExpression, Operator operator, long currentOperand) {
currentExpression.append(operator.getSymbol())
.append(currentOperand);
}

private static void removeFromExpression(StringBuilder currentExpression, Operator operator, long currentOperand) {
currentExpression.setLength(currentExpression.length() - operator.getSymbolLength() - String.valueOf(currentOperand)
.length());
}

private enum Operator {
ADDITION("+"),
SUBTRACTION("-"),
MULTIPLICATION("*"),
NONE("");

private final String symbol;
private final int symbolLength;

Operator(String symbol) {
this.symbol = symbol;
this.symbolLength = symbol.length();
}

public String getSymbol() {
return symbol;
}

public int getSymbolLength() {
return symbolLength;
}
}

private static class Equation {

private final String digits;
private final int target;

public Equation(String digits, int target) {
this.digits = digits;
this.target = target;
}

public String getDigits() {
return digits;
}

public int getTarget() {
return target;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.baeldung.backtracking;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class BacktrackingUnitTest {

private static Stream<Arguments> equationsWithNoSolutions() {
return Stream.of(Arguments.of("3456237490", 9191), Arguments.of("5", 0));
}

@ParameterizedTest
@MethodSource("equationsWithNoSolutions")
void givenEquationsWithNoSolutions_whenProcess_thenEmptyListIsReturned(String digits, int target) {
final List<String> result = Backtracking.process(digits, target);
assertTrue(result.isEmpty());
}

private static Stream<Arguments> equationsWithValidSolutions() {
return Stream.of(Arguments.of("1", 1, Collections.singletonList("1")), Arguments.of("00", 0, Arrays.asList("0+0", "0-0", "0*0")),
Arguments.of("123", 6, Arrays.asList("1+2+3", "1*2*3")), Arguments.of("232", 8, Arrays.asList("2*3+2", "2+3*2")),
Arguments.of("534", -7, Collections.singletonList("5-3*4")), Arguments.of("1010", 20, Collections.singletonList("10+10")),
Arguments.of("1234", 10, Arrays.asList("1+2+3+4", "1*2*3+4")), Arguments.of("1234", -10, Collections.singletonList("1*2-3*4")),
Arguments.of("12345", 15, Arrays.asList("1+2+3+4+5", "1*2*3+4+5", "1-2*3+4*5", "1+23-4-5")));
}

@ParameterizedTest
@MethodSource("equationsWithValidSolutions")
void givenEquationsWithValidSolutions_whenProcess_thenValidResultsAreReturned(String digits, int target, List<String> expectedSolutions) {
final List<String> result = Backtracking.process(digits, target);

assertEquals(expectedSolutions.size(), result.size());

expectedSolutions.stream()
.map(result::contains)
.forEach(Assertions::assertTrue);
}

}
Loading