Deephaven's column types

Deephaven tables store data in strongly typed columns. Each column in a table must have a specific type that determines what values it can hold, how much memory it uses, and how operations behave on that column's data. Understanding column types is essential for designing efficient table schemas, writing correct queries, and optimizing performance. This guide covers the column types available in Deephaven tables, how to convert between them, how types behave in table operations, and how to choose the right type for your data.

Column type overview

Deephaven table columns support a rich type system built on Java's type system. The main column type categories are:

Type categoryExamplesNullableMemory efficientCommon use cases
Primitive numericbyte, short, int, long, float, double✅ (sentinel null values)✅Numeric calculations, counters, measurements
Boolean/charBoolean, char✅ char uses a sentinel null value. Boolean uses Java null.✅Flags, single characters
StringString✅⚠️ Each distinct String object takes its own memory. Repeated string literals share one object.Text data, identifiers, categories
TemporalInstant, ZonedDateTime, LocalDate, LocalTime, Duration✅⚠️ Instant is stored as a long. Other temporal types are stored as objects.Timestamps, dates, times, durations
ObjectBigDecimal, BigInteger, custom classes✅❌High-precision math, complex data structures
Arrayint[], double[], String[]✅ (array itself or elements)⚠️ Each array is a separate Java object. Primitive arrays store their elements as raw values.Lists of values, time series

The rest of this guide explores each of these column types, their properties, and how to use them effectively in your tables.

Type fundamentals

Numeric primitives, booleans, characters, strings, and temporal (date and time) values are the types most columns use, so understanding them is essential for working effectively with Deephaven tables.

Note

In query strings, string literals go inside backticks, like `Hello`, and char literals go inside single quotes, like 'A'. Backticks work the same way as double quotes in Java. See String and char literals for details.

Primitive numeric types

Primitive numeric types are the most memory-efficient and performant types in Deephaven. They map directly to Java primitives, so each value is stored as a raw number rather than as a separate Java object.

Numeric type reference

TypeSizeRangeNull valueExample use case
byte8-bit-127 to 127NULL_BYTESmall integers, status codes
short16-bit-32,767 to 32,767NULL_SHORTMedium integers, year values
int32-bit-2³¹+1 to 2³¹-1NULL_INTStandard integers, counters
long64-bit-2⁶³+1 to 2⁶³-1NULL_LONGLarge integers, IDs, nanosecond timestamps
float32-bit~±3.4×10³⁸ (about 7 significant digits)NULL_FLOATApproximate decimals, measurements
double64-bit~±1.7×10³⁰⁸ (about 15 significant digits)NULL_DOUBLEGeneral-purpose decimals, measurements, statistics

Deephaven reserves the minimum value of each integer type as that type's null value, so the integer ranges above exclude it. For float and double, the null value is the most negative finite value (-Float.MAX_VALUE and -Double.MAX_VALUE).

Creating numeric columns

The examples on this page build values from the special variable ii, which holds each row's index as a long.

Null values for primitives

Java primitives can't be null, so Deephaven reserves one value of each numeric primitive type, and of char, to represent null. The numeric types use the values described above, and char uses Character.MAX_VALUE. These reserved values are called sentinel null values. Each type's sentinel has a named null constant, such as NULL_INT and NULL_DOUBLE:

You can check for nulls with isNull or by comparing with the null constant:

float and double columns can also hold NaN, which is a different value from null.

Type promotion and arithmetic

When you do arithmetic with mixed numeric types, Deephaven follows Java's type promotion rules for most operators. Division is different. In query strings, dividing one integer value by another with / returns a double. In Java, int / int truncates instead. To get integer truncation, cast the result explicitly:

Boolean and char

Boolean type

The Boolean type represents true/false values. Boolean columns have the data type java.lang.Boolean. Their null value is Java null, and the null constant NULL_BOOLEAN refers to the same value. Deephaven stores their values compactly as bytes, so they don't carry the per-value cost of the object types. Comparison expressions produce Boolean columns:

Char type

The char type holds a single 16-bit UTF-16 code unit, the same as a Java char:

Note

char is distinct from String. A char column holds single characters, while a String column holds character sequences.

String type

The String type stores text data.

Creating string columns

String operations

Values in a String column are java.lang.String objects, so you can call standard Java String methods on them in query strings:

Strings and memory

Each value in a String column refers to a String object, so a column's memory use depends on how many separate objects it holds. Deephaven doesn't deduplicate (intern) strings that formulas build at runtime:

  • A formula that builds strings, such as by concatenation, creates a new String object for each row, even when values repeat. A column of computed IDs stores one object per row.
  • A string literal in a formula, such as `Active` in the example below, is one object shared across rows. A low-cardinality column (one with few distinct values) built from a few string literals stores only a few objects.

Null strings

A null string is different from an empty string:

Temporal types

Deephaven provides robust support for the date and time types in the following table. All of them come from Java's java.time package, and Deephaven's built-in time functions operate on them, which makes them a natural fit for time-series data and temporal analysis. In memory, Instant columns are stored compactly as nanosecond long values. The other temporal types are stored as objects.

Temporal type reference

TypeDescriptionNull supportExample value
InstantPoint in time (UTC)✅2025-01-15T10:30:45.123456789Z
ZonedDateTimeDate-time with time zone✅2025-01-15T10:30:45-05:00[America/New_York]
LocalDateDate without time or zone✅2025-01-15
LocalTimeTime without date or zone✅10:30:45.123
LocalDateTimeDate-time without zone✅2025-01-15T10:30:45
DurationTime-based duration✅PT5H30M (5 hours, 30 minutes)
PeriodDate-based period✅P1Y2M3D (1 year, 2 months, 3 days)

Working with Instant

Instant is the most commonly used temporal type, representing an instantaneous point on the timeline in UTC. Adding or subtracting a number of nanoseconds shifts an Instant, so you can use the built-in nanosecond constants SECOND, MINUTE, HOUR, DAY, and WEEK. Subtracting one Instant from another gives the difference in nanoseconds, and functions such as diffMinutes return it in other units:

Working with ZonedDateTime

Use ZonedDateTime when time zone information is important:

Important

Always pass an explicit time zone when you convert an Instant to a zone-aware or local type. The same Instant has a different local date and hour in each time zone, and daylight saving time transitions shift local clock times, so an assumed zone gives subtly wrong results.

Working with LocalDate and LocalTime

LocalDate and LocalTime are useful for date-only or time-only operations:

Advanced types

For specialized use cases, object and array columns provide flexibility beyond primitive types. Object columns hold any Java object, such as a BigDecimal, and array columns hold several values in each row.

Object types

Common object types include BigDecimal, BigInteger, and custom classes. String and every temporal type except Instant are stored as Java objects too, so the considerations below apply to them as well. This section covers the remaining object types.

Object column considerations

  • Performance: Object columns are slower than primitive columns because each value is a separate heap object accessed by reference, and operations on them can't use primitive-specialized code paths.
  • Memory: Objects use more memory than primitives because each value carries an object header in addition to its data.
  • Null handling: Objects can be Java null rather than a sentinel value.

Working with BigDecimal

Use BigDecimal when you need exact decimal arithmetic that double can't provide. The following example creates a BigDecimal column and multiplies it by another BigDecimal. Because BigDecimal isn't imported into query strings by default, the formulas use its full class name:

Array types

Array columns store arrays as values, allowing each cell to contain a list of items. Operations such as groupBy produce Deephaven vector columns (for example, IntVector) rather than Java arrays. See Arrays vs. vectors for the differences.

Creating array columns

Working with array columns

Access array elements and properties:

Array null handling

Arrays themselves can be null. Elements of object arrays can be null, and elements of primitive arrays can hold the type's null value (for example, NULL_INT):

Array operations

Deephaven's built-in functions, such as the math functions sum, avg, min, and max, operate on arrays:

Type conversions and casting

The most common ways a numeric or string value changes type are:

  • Widening: A numeric value widens to a larger numeric type automatically in arithmetic, as described in Type promotion and arithmetic.
  • Narrowing: A numeric value narrows, for example from double to int, only with an explicit cast, and it can lose precision.
  • Parsing: A string never becomes a number automatically. Convert it with a parsing function such as parseInt, or with a constructor such as new java.math.BigDecimal(`1.10`).
  • Formatting: A number becomes a string when you concatenate it with a string or format it explicitly.

Narrowing with explicit casts

Narrowing conversions need an explicit cast. Casting a double to an integer type truncates the fractional part:

Parsing strings to numbers

Convert strings to numeric types:

Formatting numbers as strings

Convert numbers to strings:

Handling conversion errors

Parsing functions such as parseInt return a null value for a null string, but throw a NumberFormatException for a string that isn't a valid number. Check the string before you parse it:

Using types effectively

Understanding how types behave in table operations helps you write queries that perform well and are easy to maintain. This section shows how column types affect aggregations, joins, and sorts, then gives guidelines for choosing types, keeping queries type-safe, and keeping queries fast.

Aggregations and types

An aggregation's output column doesn't always have its input column's type. The following aggBy example uses three aggregation operators:

  • AggSum returns a long for an int column and a double for a double column.
  • AggAvg returns a double.
  • AggCount returns a long.

Joins and type matching

Joins such as naturalJoin require matching types in key columns:

Type mismatches cause errors:

Cast to matching types when necessary:

Sorting and type behavior

Sorting orders each column by its type: numbers by value, strings lexicographically, and temporal values chronologically. In an ascending sort, null values come first:

Choose the right type

  • Integers: Use the smallest type that fits your range (byte < short < int < long).
  • Decimals: Use double for most cases, BigDecimal only when exact precision is required.
  • Timestamps: Use Instant to store points in time, and pass a time zone to functions such as toLocalDate or hourOfDay when you need local values. Use ZonedDateTime columns only when each value must carry its own time zone.
  • Text: Use String for text data. Store low-cardinality categories as String values or small integer codes.
  • Arrays: Use arrays when each row needs multiple related values. Use separate columns instead if you query individual elements often.

Type safety

  • Handle nulls explicitly, for example with isNull or a comparison with the type's null constant.
  • Use explicit casts when converting between types to make intent clear.
  • Match types in join keys. Comparisons between different numeric types work without a cast.
  • Test edge cases (nulls, extreme values, type boundaries).

Performance considerations

ConcernEfficientLess efficient
Arithmetic✅ int, long, double❌ BigDecimal
Aggregations✅ Numeric primitives❌ Objects
Memory usage✅ Primitives, repeated string literals❌ Objects, large arrays