+ * Vector properties do not map cleanly to an existing class in the standard JDK Collections hierarchy. Vectors when + * used with embeddings (machine learning) represent an opaque point in the vector space that does not expose meaningful + * properties nor guarantees computational values to the outside world. + *
+ * Vectors should be treated as opaque values and should not be modified. They can be created from an array of numbers + * (typically {@code double} or {@code float} values) and used by components that need to provide the vector for storage + * or computation. + * + * @author Mark Paluch + * @since 3.5 + */ +public interface Vector { + + /** + * Creates a new {@link Vector} from the given float {@code values}. Vector values are duplicated to avoid capturing a + * mutable array instance and to prevent mutability. + * + * @param values float vector values. + * @return the {@link Vector} for the given vector values. + */ + static Vector of(float... values) { + + Assert.notNull(values, "float vector values must not be null"); + + return FloatVector.copy(values); + } + + /** + * Creates a new {@link Vector} from the given double {@code values}. Vector values are duplicated to avoid capturing + * a mutable array instance and to prevent mutability. + * + * @param values double vector values. + * @return the {@link Vector} for the given vector values. + */ + static Vector of(double... values) { + + Assert.notNull(values, "double vector values must not be null"); + + return DoubleVector.copy(values); + } + + /** + * Creates a new {@link Vector} from the given number {@code values}. Vector values are duplicated to avoid capturing + * a mutable collection instance and to prevent mutability. + * + * @param values number vector values. + * @return the {@link Vector} for the given vector values. + */ + static Vector of(Collection extends Number> values) { + + Assert.notNull(values, "Vector values must not be null"); + if(values.isEmpty()) { + return NumberVector.copy(new Number[0]); + } + + Class> cet = CollectionUtils.findCommonElementType(values); + + if (cet == Double.class) { + return DoubleVector.copy(values); + } + + if (cet == Float.class) { + return FloatVector.copy(values); + } + + return NumberVector.copy(values); + } + + /** + * Creates a new unsafe {@link Vector} wrapper from the given {@code values}. Unsafe wrappers do not duplicate array + * values and are merely a view on the source array. + *
+ * Supported source type + * + * @param values vector values. + * @return the {@link Vector} for the given vector values. + */ + static Vector unsafe(float[] values) { + + Assert.notNull(values, "float vector values must not be null"); + + return new FloatVector(values); + } + + /** + * Creates a new unsafe {@link Vector} wrapper from the given {@code values}. Unsafe wrappers do not duplicate array + * values and are merely a view on the source array. + *
+ * Supported source type + * + * @param values vector values. + * @return the {@link Vector} for the given vector values. + */ + static Vector unsafe(double[] values) { + + Assert.notNull(values, "double vector values must not be null"); + + return new DoubleVector(values); + } + + /** + * Returns the type of the underlying vector source. + * + * @return the type of the underlying vector source. + */ + Class extends Number> getType(); + + /** + * Returns the source array of the vector. The source array is not copied and should not be modified to avoid + * mutability issues. This method should be used for performance access. + * + * @return the source array of the vector. + */ + Object getSource(); + + /** + * Returns the number of dimensions. + * + * @return the number of dimensions. + */ + int size(); + + /** + * Convert the vector to a {@code float} array. The returned array is a copy of the {@link #getSource() source} array + * and can be modified safely. + *
+ * Conversion to {@code float} can incorporate loss of precision or result in values with a slight offset due to data + * type conversion if the source is not a {@code float} array. + * + * @return a new {@code float} array representing the vector point. + */ + float[] toFloatArray(); + + /** + * Convert the vector to a {@code double} array. The returned array is a copy of the {@link #getSource() source} array + * and can be modified safely. + *
+ * Conversion to {@code double} can incorporate loss of precision or result in values with a slight offset due to data
+ * type conversion if the source is not a {@code double} array.
+ *
+ * @return a new {@code double} array representing the vector point.
+ */
+ double[] toDoubleArray();
+
+}
diff --git a/src/test/java/org/springframework/data/domain/DoubleVectorUnitTests.java b/src/test/java/org/springframework/data/domain/DoubleVectorUnitTests.java
new file mode 100644
index 0000000000..b87c320d72
--- /dev/null
+++ b/src/test/java/org/springframework/data/domain/DoubleVectorUnitTests.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.domain;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link DoubleVector}.
+ *
+ * @author Mark Paluch
+ */
+class DoubleVectorUnitTests {
+
+ double[] values = new double[] { 1.1, 2.2, 3.3, 4.4, 5.5 };
+ float[] floats = new float[] { (float) 1.1d, (float) 2.2d, (float) 3.3d, (float) 4.4d, (float) 5.5d };
+
+ @Test // GH-3193
+ void shouldCreateVector() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.size()).isEqualTo(5);
+ assertThat(vector.getType()).isEqualTo(Double.TYPE);
+ }
+
+ @Test // GH-3193
+ void shouldCreateUnsafeVector() {
+
+ Vector vector = Vector.unsafe(values);
+
+ assertThat(vector.getSource()).isSameAs(values);
+ }
+
+ @Test // GH-3193
+ void shouldCopyVectorValues() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.getSource()).isNotSameAs(vector).isEqualTo(values);
+ }
+
+ @Test // GH-3193
+ void shouldRenderToString() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector).hasToString("D[1.1, 2.2, 3.3, 4.4, 5.5]");
+ }
+
+ @Test // GH-3193
+ void shouldCompareVector() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector).isEqualTo(Vector.of(values));
+ assertThat(vector).hasSameHashCodeAs(Vector.of(values));
+ }
+
+ @Test // GH-3193
+ void sourceShouldReturnSource() {
+
+ Vector vector = new DoubleVector(values);
+
+ assertThat(vector.getSource()).isSameAs(values);
+ }
+
+ @Test // GH-3193
+ void shouldCreateFloatArray() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.toFloatArray()).isEqualTo(floats).isNotSameAs(floats);
+ }
+
+ @Test // GH-3193
+ void shouldCreateDoubleArray() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.toDoubleArray()).isEqualTo(values).isNotSameAs(values);
+ }
+}
diff --git a/src/test/java/org/springframework/data/domain/FloatVectorUnitTests.java b/src/test/java/org/springframework/data/domain/FloatVectorUnitTests.java
new file mode 100644
index 0000000000..c58d5d047b
--- /dev/null
+++ b/src/test/java/org/springframework/data/domain/FloatVectorUnitTests.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.domain;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link FloatVector}.
+ *
+ * @author Mark Paluch
+ */
+class FloatVectorUnitTests {
+
+ float[] values = new float[] { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f };
+ double[] doubles = new double[] { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f };
+
+ @Test // GH-3193
+ void shouldCreateVector() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.size()).isEqualTo(5);
+ assertThat(vector.getType()).isEqualTo(Float.TYPE);
+ }
+
+ @Test // GH-3193
+ void shouldCreateUnsafeVector() {
+
+ Vector vector = Vector.unsafe(values);
+
+ assertThat(vector.getSource()).isSameAs(values);
+ }
+
+ @Test // GH-3193
+ void shouldCopyVectorValues() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.getSource()).isNotSameAs(vector).isEqualTo(values);
+ }
+
+ @Test // GH-3193
+ void shouldRenderToString() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector).hasToString("F[1.1, 2.2, 3.3, 4.4, 5.5]");
+ }
+
+ @Test // GH-3193
+ void shouldCompareVector() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector).isEqualTo(Vector.of(values));
+ assertThat(vector).hasSameHashCodeAs(Vector.of(values));
+ }
+
+ @Test // GH-3193
+ void sourceShouldReturnSource() {
+
+ Vector vector = new FloatVector(values);
+
+ assertThat(vector.getSource()).isSameAs(values);
+ }
+
+ @Test // GH-3193
+ void shouldCreateFloatArray() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.toFloatArray()).isEqualTo(values).isNotSameAs(values);
+ }
+
+ @Test // GH-3193
+ void shouldCreateDoubleArray() {
+
+ Vector vector = Vector.of(values);
+
+ assertThat(vector.toDoubleArray()).isEqualTo(doubles).isNotSameAs(doubles);
+ }
+}
diff --git a/src/test/java/org/springframework/data/domain/NumberVectorUnitTests.java b/src/test/java/org/springframework/data/domain/NumberVectorUnitTests.java
new file mode 100644
index 0000000000..ba730de0fc
--- /dev/null
+++ b/src/test/java/org/springframework/data/domain/NumberVectorUnitTests.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.domain;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link NumberVector}.
+ *
+ * @author Mark Paluch
+ * @author Christoph Strobl
+ */
+class NumberVectorUnitTests {
+
+ Number[] values = new Number[] { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6f };
+ Number[] floats = new Number[] { (float) 1.1d, (float) 2.2d, (float) 3.3d, (float) 4.4d, (float) 5.5, 6.6 };
+
+ @Test // GH-3193
+ void shouldErrorOnNullElements() {
+
+ List